/* ==========================================================================
   Aurora voting template - styles.css
   Anything Tailwind's CDN build can't reasonably express lives here:
   brand CSS custom properties, the fixed background layer, the basket
   panel/sheet animation choreography, list reorder animations, skeletons
   and reduced-motion overrides.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Brand tokens (defaults - overwritten at runtime from data/config.json
   by js/modules/theme.js so a brand swap never touches this file)
   -------------------------------------------------------------------------- */
:root {
  --color-primary: #ff3d6e;
  --color-primary-contrast: #ffffff;
  --color-secondary: #151033;
  --color-surface: #ffffff;
  --color-surface-alt: #f4f3fa;
  --color-text: #161226;
  --color-text-muted: #5c5872;
  --color-accent: #ffd23f;

  --font-display: Georgia, 'Times New Roman', serif;
  --font-body: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  --overlay-opacity: 0.45;

  --anim-fast: 180ms;
  --anim-base: 220ms;
  --anim-slow: 300ms;
  --ease-in: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: ease-out;
}

* {
  box-sizing: border-box;
}

html,
body {
  overflow-x: hidden;
}

html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-body);
}

/* --------------------------------------------------------------------------
   2. Fixed background layer
   -------------------------------------------------------------------------- */
.page-background {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
}

.page-background picture,
.page-background img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.page-background__overlay {
  position: absolute;
  inset: 0;
  background: var(--color-secondary);
  opacity: var(--overlay-opacity);
}

/* --------------------------------------------------------------------------
   2a. Split hero (brand.hero.enabled in config.json)
   Two stacked panels on mobile (brand panel on top, info panel below); a
   two-column split on desktop (info panel left, brand panel right). The
   "start voting" CTA is nested inside the brand panel and anchored to its
   bottom edge with one unified rule (see .hero-split__cta below) - no
   breakpoint-specific positioning, so it looks/behaves identically at
   every size. Sits right after the sticky header, in normal document flow.
   -------------------------------------------------------------------------- */
.hero-split {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--color-secondary);
  /* Not overflow: hidden - on the desktop row layout the two panels stretch
     to equal height, so .hero-split__cta's bottom edge lines up with the
     hero's own bottom edge; it needs to be free to protrude past it. */
}

.hero-split__panel {
  position: relative;
  padding: 2.5rem 1.5rem 3rem;
}

.hero-split__panel--brand {
  order: 1;
  /* Not overflow: hidden - the .hero-split__cta (2c) is nested in here and
     deliberately overflows the panel's own bottom edge by half its height. */
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 420px;
  padding-bottom: 4rem;
}

/* Transparent - the fixed page background shows through around the info
   card, which carries its own solid background below. */
.hero-split__panel--info {
  order: 3;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-split__info-card {
  background: rgba(0, 0, 0, 1);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  max-width: 32rem;
  padding: 2rem 1.75rem;
  border-radius: 1rem;
}

.hero-split__bg {
  position: absolute;
  inset: 0;
}

.hero-split__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-split__dots {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(0, 0, 0, 0.55) 1.5px, transparent 1.5px);
  background-size: 16px 16px;
  opacity: 0.35;
  mask-image: linear-gradient(to bottom right, transparent 15%, black 55%);
}

.hero-split__brand-content {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: center;
}

/* image-mobile.png is transparent - it sits over .hero-split__bg's gradient
   + dots. image-desktop.png (served via that same img's <picture> source
   at 1024px+) is opaque and self-contained, so this graphic is mobile-only -
   hidden once the desktop background takes over below. */
.hero-split__brand-graphic {
  width: min(78vw, 360px);
  height: auto;
  display: block;
  filter: drop-shadow(0 12px 40px rgba(0, 0, 0, 0.35));
}

.hero-split__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.9rem;
  line-height: 1.15;
  color: #fff;
  margin: 0 0 0.85rem;
}

.hero-split__intro {
  color: rgba(255, 255, 255, 0.75);
  font-size: 0.95rem;
  line-height: 1.55;
  margin: 0 0 1.5rem;
  max-width: 32rem;
}

.hero-split__steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  counter-reset: step;
  max-width: 32rem;
}

.hero-split__step {
  counter-increment: step;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 0.85rem;
  padding: 0.9rem 1.1rem 0.9rem 3.25rem;
}

.hero-split__step::before {
  content: counter(step);
  position: absolute;
  left: 0.9rem;
  top: 0.9rem;
  width: 1.65rem;
  height: 1.65rem;
  border-radius: 999px;
  background: var(--color-primary);
  color: #fff;
  font-weight: 800;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-split__step-title {
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
}

.hero-split__step-desc {
  color: rgba(255, 255, 255, 0.65);
  font-size: 0.85rem;
  line-height: 1.4;
}

/* Nested inside .hero-split__panel--brand (which is position: relative) and
   anchored to its bottom edge - this single rule is identical on mobile and
   desktop, no breakpoint override needed. */
.hero-split__cta {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  color: #fff;
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
}

.hero-split__cta svg {
  animation: hero-chevron 1.8s ease-in-out infinite;
}

.hero-split__cta:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

@keyframes hero-chevron {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(6px);
  }
}

/* Desktop: two-column split, CTA overlaid on the seam between panels */
@media (min-width: 1024px) {
  .hero-split {
    flex-direction: row;
    align-items: stretch;
  }

  .hero-split__panel {
    flex: 1 1 50%;
    min-width: 0;
    padding: 4.5rem 4vw 5rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  .hero-split__panel--brand {
    order: 2;
    min-height: 0;
    padding-bottom: 5rem;
  }

  /* image-desktop.png (set as #hero-background's source above) is a
     self-contained graphic with its own gradient + dots baked in, so the
     mobile-only overlay graphic and the CSS-drawn dot texture both stand
     down here. */
  .hero-split__brand-graphic,
  .hero-split__dots {
    display: none;
  }

  .hero-split__panel--info {
    order: 1;
  }

  /* .hero-split__cta is positioned relative to .hero-split__panel--brand
     (the right column). Its left edge sits exactly at the hero's own
     horizontal midpoint (the seam), so left: 0 here - not 50% - centers the
     CTA on the whole hero rather than on just the right column. */
  .hero-split__cta {
    left: 0;
  }

  .hero-split__title {
    font-size: 2.75rem;
  }
}

/* --------------------------------------------------------------------------
   3. Skip links
   -------------------------------------------------------------------------- */
.skip-links {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
}

.skip-link {
  position: absolute;
  left: 0;
  top: -100px;
  background: var(--color-accent);
  color: var(--color-secondary);
  font-weight: 700;
  padding: 0.75rem 1.25rem;
  border-radius: 0 0 0.5rem 0;
  white-space: nowrap;
  transition: top var(--anim-fast) var(--ease-out);
}

.skip-link:focus {
  top: 0;
  outline: 3px solid var(--color-secondary);
  outline-offset: -3px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   4. Header
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(21, 16, 51, 0.85);
  backdrop-filter: blur(6px);
}

@media (max-width: 639px) {
  .site-header .max-w-6xl {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }
}

/* --------------------------------------------------------------------------
   5. List panel - shares one dark card between the search bar and the song
   grid (see 7). Pagination navs (see 8) sit outside it, above and below.
   -------------------------------------------------------------------------- */
.list-panel {
  background: rgba(23, 16, 33, 1);
  border-radius: 1.25rem;
  padding: 1.5rem 1.25rem 2rem;
}

@media (min-width: 768px) {
  .list-panel {
    padding: 2rem 2rem 2.5rem;
  }
}

/* --------------------------------------------------------------------------
   6. Search
   The list/search area sits inside .list-panel (5), so its chrome (input,
   results count, empty state) uses light-on-dark colors rather than the
   --color-text tokens (those stay reserved for text on light surfaces, e.g.
   inside white song cards).
   -------------------------------------------------------------------------- */
.search-icon {
  position: absolute;
  left: 1.1rem;
  top: 50%;
  transform: translateY(-50%);
  color: rgba(255, 255, 255, 0.5);
  pointer-events: none;
}

.search-input {
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 0.9rem;
  background: #000;
  color: #fff;
}

.search-input::placeholder {
  color: #fff;
  opacity: 1;
}

.search-clear {
  position: absolute;
  right: 0.6rem;
  top: 50%;
  transform: translateY(-50%);
  width: 2rem;
  height: 2rem;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.1);
}

.search-clear:hover {
  color: #fff;
}

.results-count {
  color: rgba(255, 255, 255, 0.55);
}

.empty-state__message {
  color: rgba(255, 255, 255, 0.85);
}

mark {
  background: color-mix(in srgb, var(--color-accent) 55%, transparent);
  color: inherit;
  border-radius: 0.15em;
  padding: 0 0.05em;
}

/* --------------------------------------------------------------------------
   7. Song grid + card
   Square artwork on top, a white meta bar below (title/artist left, a
   round vote toggle right). 2 columns on mobile, 4 on desktop - deliberately
   no 3-column tier in between.
   -------------------------------------------------------------------------- */
.song-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.85rem;
  list-style: none;
  padding: 0;
}

@media (min-width: 768px) {
  .song-grid {
    grid-template-columns: repeat(4, 1fr);
    gap: 1.5rem;
  }
}

/* Unselected: a black card with a white "+" toggle. Selected: the whole
   card inverts to a white card (with a purple ring + a filled purple
   toggle showing the pick's position) - the "add a track" inversion. */
.song-card {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  grid-template-areas: 'art art' 'meta action';
  min-width: 0;
  background: var(--color-secondary);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.25);
  border: 2px solid transparent;
  transition: background var(--anim-fast) var(--ease-out), border-color var(--anim-fast) var(--ease-out);
}

.song-card:has(.song-card__action[data-state='added']) {
  background: var(--color-surface);
  border-color: var(--color-primary);
}

.song-card__artwork-wrap {
  grid-area: art;
  aspect-ratio: 1 / 1;
  background: var(--color-surface-alt);
  overflow: hidden;
  border-radius: 10px 10px 0 0;
}

.song-card__artwork {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border: 2px solid #fff;
  border-radius: 10px 10px 0 0;
}

.song-card__meta {
  grid-area: meta;
  min-width: 0;
  padding: 0.6rem 0.25rem 0.6rem 0.75rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: left;
}

.song-card__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
  line-height: 1.25;
  color: #fff;
  overflow-wrap: break-word;
  transition: color var(--anim-fast) var(--ease-out);
}

.song-card:has(.song-card__action[data-state='added']) .song-card__title {
  color: var(--color-text);
}

@media (min-width: 768px) {
  .song-card__title {
    font-size: 1.05rem;
  }
}

.song-card__artist {
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.72rem;
  margin-top: 0.2rem;
  overflow-wrap: break-word;
  transition: color var(--anim-fast) var(--ease-out);
}

.song-card:has(.song-card__action[data-state='added']) .song-card__artist {
  color: var(--color-text-muted);
}

@media (min-width: 768px) {
  .song-card__artist {
    font-size: 0.8rem;
  }
}

.song-card__action-wrap {
  grid-area: action;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.6rem 0.6rem 0.6rem 0.25rem;
}

/* Default (unselected) state: a white circle with a "+". Selected flips
   this to a filled purple circle with the pick's position number - the
   colour inversion the design calls for when a track is chosen. */
.song-card__action {
  width: 2.25rem;
  height: 2.25rem;
  min-width: 2.25rem;
  min-height: 2.25rem;
  padding: 0;
  border-radius: 999px;
  border: none;
  font-weight: 800;
  font-size: 1rem;
  line-height: 1;
  background: #fff;
  color: var(--color-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--anim-fast) var(--ease-out), filter var(--anim-fast), background var(--anim-fast), color var(--anim-fast);
}

@media (min-width: 768px) {
  .song-card__action {
    width: 2.5rem;
    height: 2.5rem;
    min-width: 2.5rem;
    min-height: 2.5rem;
  }
}

.song-card__action:hover:not(:disabled) {
  filter: brightness(0.95);
}

.song-card__action:active:not(:disabled) {
  transform: scale(0.9);
}

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

.song-card__action[data-state='added'] {
  background: var(--color-primary);
  color: #fff;
  font-size: 0.85rem;
}

.song-card__action:disabled {
  background: #fff;
  color: rgba(0, 0, 0, 0.25);
  cursor: not-allowed;
}

/* Skeleton loading state */
.song-card--skeleton {
  box-shadow: none;
}

.skeleton-block,
.skeleton-line {
  background: linear-gradient(
    90deg,
    var(--color-surface-alt) 25%,
    #e9e8f0 37%,
    var(--color-surface-alt) 63%
  );
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.4s ease infinite;
  border-radius: 0.4rem;
}

.skeleton-line {
  height: 0.9rem;
  margin: 0.85rem 1rem 0;
}

.skeleton-line--artist {
  width: 60%;
  margin-top: 0.5rem;
  margin-bottom: 1rem;
}

@keyframes skeleton-shimmer {
  0% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0 50%;
  }
}

/* --------------------------------------------------------------------------
   8. Pagination
   -------------------------------------------------------------------------- */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 2rem 0;
  flex-wrap: wrap;
}

/* On desktop the hero's "start voting" CTA already overlaps the hero's own
   bottom edge by half its height - give it a lot more breathing room before
   the pagination/search panel below, matching design.png. */
@media (min-width: 1024px) {
  .pagination-nav--top {
    margin-top: 6rem;
  }
}

.page-btn,
.page-number {
  min-width: 40px;
  min-height: 40px;
  border-radius: 999px;
  background: var(--color-surface-alt);
  color: var(--color-secondary);
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.page-btn:hover:not(:disabled),
.page-number:hover:not([aria-current='page']) {
  filter: brightness(0.94);
}

.page-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* Only prev/next flank the numbers in this design - the double-chevron
   first/last controls stay in the DOM (keyboard/URL navigation still
   reaches them) but are visually hidden. */
.page-btn--edge {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

.page-number[aria-current='page'] {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

/* Same numbered-circle pagination at every breakpoint - no separate
   "page X / Y" text indicator on mobile. */
.page-numbers {
  display: flex;
  gap: 0.5rem;
}

.page-indicator {
  display: none;
}

.page-ellipsis {
  color: rgba(255, 255, 255, 0.5);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
}

/* --------------------------------------------------------------------------
   9. Footer
   -------------------------------------------------------------------------- */
.site-footer {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.footer-link {
  color: rgba(255, 255, 255, 0.8);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer-link:hover {
  color: #fff;
}

.footer-legal-notice {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  color: rgba(255, 255, 255, 0.55);
}

.footer-legal-notice__link {
  color: rgba(255, 255, 255, 0.8);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer-legal-notice__link:hover {
  color: #fff;
}

/* --------------------------------------------------------------------------
   10. Basket bar (full-width, fixed to the bottom on both mobile & desktop)
   -------------------------------------------------------------------------- */
.basket-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  background: linear-gradient(120deg, var(--color-primary), color-mix(in srgb, var(--color-primary) 65%, var(--color-secondary)));
  color: #fff;
  padding: 1rem 1.25rem calc(1rem + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -10px 30px -10px rgba(0, 0, 0, 0.5);
  transform: translateY(100%);
  opacity: 0;
  pointer-events: none;
  transition: transform var(--anim-base) var(--ease-in), opacity var(--anim-base) var(--ease-in);
}

.basket-bar.is-visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

.basket-bar__inner {
  max-width: 72rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.basket-bar__title {
  font-weight: 800;
  font-size: 1rem;
  margin: 0;
}

.basket-bar__subtitle {
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.8);
  margin: 0.1rem 0 0;
}

.basket-bar__cta {
  width: 100%;
  min-height: 52px;
  border-radius: 999px;
  background: var(--color-secondary);
  color: #fff;
  font-weight: 800;
  font-size: 0.9rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  transition: filter var(--anim-fast), transform var(--anim-fast);
}

.basket-bar__cta:hover:not(:disabled) {
  filter: brightness(1.15);
}

.basket-bar__cta:active:not(:disabled) {
  transform: scale(0.98);
}

.basket-bar__cta:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

/* Disabled until the required number of tracks is picked - see basket.js
   updateChrome(). Stays black, just dimmed, rather than switching to a
   different colour. */
.basket-bar__cta:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

@media (min-width: 768px) {
  .basket-bar {
    padding: 1.1rem 2rem calc(1.1rem + env(safe-area-inset-bottom, 0px));
  }

  .basket-bar__inner {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
  }

  .basket-bar__cta {
    width: auto;
    padding: 0 2.5rem;
  }
}

/* --------------------------------------------------------------------------
   11. Basket backdrop + panel/sheet
   -------------------------------------------------------------------------- */
body.basket-open {
  overflow: hidden;
}

.basket-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity var(--anim-base) var(--ease-out);
}

.basket-backdrop.is-visible {
  opacity: 1;
}

.basket-panel {
  position: fixed;
  z-index: 61;
  background: var(--color-surface);
  color: var(--color-text);
  display: flex;
  flex-direction: column;
}

.basket-panel[hidden] {
  display: none;
}

.basket-panel__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 1.25rem 1.25rem 0.75rem;
  border-bottom: 1px solid var(--color-surface-alt);
}

.basket-close {
  width: 40px;
  height: 40px;
  min-width: 44px;
  min-height: 44px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  margin: -8px -8px 0 0;
}

.basket-close:hover {
  background: var(--color-surface-alt);
}

.basket-panel__body {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.basket-panel__footer {
  /* The panel/sheet touches the true bottom edge of the screen, so the
     submit button gets extra bottom padding on devices that reserve a
     home-indicator strip there (falls back to 0 everywhere else). */
  padding: 1rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--color-surface-alt);
}

.basket-panel__grabber {
  display: none;
}

/* Step 2: entrant email */
.basket-email-step {
  padding: 0.5rem 1.25rem 1.5rem;
}

.basket-email-step__intro {
  color: var(--color-text-muted);
  font-size: 0.9rem;
  margin-bottom: 1.25rem;
}

.basket-email-step__label {
  display: block;
  font-weight: 700;
  font-size: 0.875rem;
  color: var(--color-text);
  margin-bottom: 0.4rem;
}

.basket-email-step__input {
  width: 100%;
  min-height: 48px;
  padding: 0 1rem;
  border-radius: 0.75rem;
  border: 1.5px solid var(--color-surface-alt);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 1rem;
}

.basket-email-step__input:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 1px;
}

.basket-email-step__input[aria-invalid='true'] {
  border-color: var(--color-primary);
}

.basket-email-step__error {
  color: var(--color-primary);
  font-size: 0.85rem;
  font-weight: 600;
  margin-top: 0.5rem;
}

.basket-email-step__consents {
  margin-top: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.basket-checkbox {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--color-text);
  line-height: 1.4;
}

.basket-checkbox input[type='checkbox'] {
  width: 20px;
  height: 20px;
  min-width: 20px;
  margin-top: 0.1rem;
  accent-color: var(--color-primary);
  cursor: pointer;
}

.basket-checkbox input[type='checkbox']:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.basket-checkbox__link,
.basket-email-step__link {
  color: var(--color-primary);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.basket-footer-email {
  display: flex;
  gap: 0.75rem;
}

/* An attribute selector on top of the class selector outranks it, so this
   wins over `.basket-footer-email { display: flex }` above regardless of
   stylesheet order - without it, `[hidden]`'s UA-stylesheet `display: none`
   would lose to our own `display: flex` and the step would stay visible. */
.basket-footer-email[hidden] {
  display: none;
}

/* Mobile: full-height bottom sheet */
@media (max-width: 767px) {
  .basket-panel {
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    border-radius: 1.25rem 1.25rem 0 0;
    transform: translateY(100%);
    transition: transform var(--anim-slow) var(--ease-in);
    padding-top: 0.5rem;
  }

  .basket-panel.is-open {
    transform: translateY(0);
  }

  .basket-panel__grabber {
    display: block;
    width: 40px;
    height: 4px;
    border-radius: 999px;
    background: var(--color-surface-alt);
    margin: 0.5rem auto 0.25rem;
  }
}

/* Desktop: right-edge side panel */
@media (min-width: 768px) {
  .basket-panel {
    top: 0;
    right: 0;
    bottom: 0;
    width: 400px;
    max-width: 92vw;
    max-height: 100vh;
    box-shadow: -20px 0 60px -15px rgba(0, 0, 0, 0.45);
    transform: translateX(24px);
    opacity: 0;
    transition: transform var(--anim-base) var(--ease-out), opacity var(--anim-base) var(--ease-out);
  }

  .basket-panel.is-open {
    transform: translateX(0);
    opacity: 1;
  }
}

/* --------------------------------------------------------------------------
   12. Basket list + reordering
   -------------------------------------------------------------------------- */
.basket-ranking-hint {
  margin: 0;
  padding: 0.75rem 1.25rem 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text-muted);
}

.basket-list {
  list-style: none;
  margin: 0;
  padding: 0.5rem 1rem 1rem;
}

.basket-item {
  display: grid;
  grid-template-columns: 1.5rem 2.75rem 1fr auto auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 0.25rem;
  border-bottom: 1px solid var(--color-surface-alt);
  background: var(--color-surface);
  position: relative;
}

.basket-item.is-removing {
  pointer-events: none;
}

.basket-item__position {
  font-weight: 800;
  color: var(--color-text-muted);
  text-align: center;
}

.basket-item__artwork {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 0.5rem;
  object-fit: cover;
  background: var(--color-surface-alt);
}

.basket-item__meta {
  min-width: 0;
}

.basket-item__title {
  font-weight: 700;
  font-size: 0.9rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.basket-item__artist {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.basket-item__reorder {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.basket-item__arrow {
  width: 40px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  color: var(--color-text-muted);
  background: var(--color-surface-alt);
  border-radius: 0.6rem;
  transition: transform 120ms ease-out, background 120ms ease-out, color 120ms ease-out;
}

.basket-item__arrow:hover:not(:disabled) {
  color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 12%, var(--color-surface-alt));
}

.basket-item__arrow:active:not(:disabled) {
  transform: scale(0.94);
}

.basket-item__arrow:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.basket-item__remove {
  width: 36px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
}

.basket-item__remove:hover {
  color: var(--color-primary);
}

.basket-item__arrow:focus-visible,
.basket-item__remove:focus-visible,
.basket-close:focus-visible,
.basket-fab:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Reordering happens by tapping, so the arrows get generous touch targets
   here - 44px is the minimum recommended hit area for a finger tap. */
@media (max-width: 767px) {
  .basket-item {
    grid-template-columns: 1.5rem 2.75rem 1fr auto auto;
    gap: 0.6rem;
    padding: 0.85rem 0.25rem;
  }

  .basket-item__reorder {
    gap: 0.4rem;
  }

  .basket-item__arrow {
    width: 46px;
    height: 44px;
    font-size: 0.9rem;
  }

  .basket-item__remove {
    width: 40px;
    height: 44px;
  }
}

/* Entering / leaving basket items */
.basket-item.is-entering {
  animation: item-enter var(--anim-base) var(--ease-in);
}

@keyframes item-enter {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.basket-item.is-leaving {
  animation: item-leave var(--anim-fast) var(--ease-out) forwards;
}

@keyframes item-leave {
  from {
    opacity: 1;
    max-height: 80px;
  }
  to {
    opacity: 0;
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin: 0;
  }
}

/* --------------------------------------------------------------------------
   13. Toasts / inline errors
   -------------------------------------------------------------------------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(7.5rem + env(safe-area-inset-bottom, 0px));
  transform: translate(-50%, 8px);
  z-index: 70;
  background: var(--color-secondary);
  color: #fff;
  padding: 0.75rem 1.25rem;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 600;
  box-shadow: 0 10px 24px -8px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity var(--anim-fast) var(--ease-out), transform var(--anim-fast) var(--ease-out);
}

.toast.is-visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

.submit-error {
  position: fixed;
  left: 50%;
  bottom: calc(7.5rem + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 70;
  background: #fff;
  color: var(--color-text);
  padding: 0.9rem 1.1rem;
  border-radius: 0.9rem;
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  max-width: 90vw;
}

.submit-retry {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  font-weight: 700;
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   13a. Cookie consent banner
   -------------------------------------------------------------------------- */
.cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  background: var(--color-surface);
  color: var(--color-text);
  padding: 1.1rem 1.5rem calc(1.1rem + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -10px 30px -10px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--anim-base) var(--ease-in), transform var(--anim-base) var(--ease-in);
}

.cookie-banner.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.cookie-banner__message {
  max-width: 44rem;
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--color-text-muted);
  text-align: center;
  margin: 0;
}

.cookie-banner__actions {
  display: flex;
  justify-content: center;
  gap: 0.75rem;
  flex-shrink: 0;
}

.cookie-banner__btn {
  min-height: 42px;
  padding: 0 1.25rem;
  border-radius: 999px;
  font-weight: 700;
  font-size: 0.875rem;
  white-space: nowrap;
}

.cookie-banner__btn--primary {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

.cookie-banner__btn--primary:hover {
  filter: brightness(1.1);
}

.cookie-banner__btn--ghost {
  background: var(--color-surface-alt);
  color: var(--color-text);
}

.cookie-banner__btn--ghost:hover {
  filter: brightness(0.96);
}

.cookie-banner__btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

@media (max-width: 639px) {
  .cookie-banner {
    gap: 0.65rem;
    padding: 0.9rem 1.1rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
  }

  .cookie-banner__message {
    font-size: 0.78rem;
  }

  .cookie-banner__btn {
    min-height: 40px;
    padding: 0 1rem;
    font-size: 0.82rem;
  }
}

/* --------------------------------------------------------------------------
   14. Success screen
   -------------------------------------------------------------------------- */
.success-screen {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: rgba(10, 6, 26, 0.72);
  backdrop-filter: blur(4px);
}

.success-screen__card {
  max-width: 32rem;
  width: 100%;
  text-align: center;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 1.5rem;
  padding: 2.5rem 1.75rem;
  animation: success-in var(--anim-slow) var(--ease-in);
}

@keyframes success-in {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.success-vote-list {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  text-align: left;
  display: grid;
  gap: 0.5rem;
}

.success-vote-list__item {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 0.6rem;
  padding: 0.5rem 0.75rem;
  color: #fff;
}

.success-vote-list__position {
  font-weight: 800;
  color: var(--color-accent);
  min-width: 1.25rem;
}

.success-vote-list__artist {
  color: rgba(255, 255, 255, 0.7);
  font-size: 0.85rem;
}

.success-screen__actions {
  margin-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
}

/* --------------------------------------------------------------------------
   15. Focus visibility
   -------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   16. Reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
