/* =========================================================================
   Song request console — shared design system
   Palette borrowed from the DJ booth itself: plum-dark housing, amber cue
   lamp, mint signal LED, rose for anything destructive.
   ========================================================================= */

/* The UA stylesheet's [hidden] { display: none } loses to any author display
   rule, so a component styled `display: flex` ignores the hidden attribute
   entirely. Restate it with !important so `el.hidden = true` always works. */
[hidden] {
  display: none !important;
}

:root {
  --ink: #141122;
  --ink-deep: #0D0B17;
  --panel: #1E1A33;
  --panel-hi: #262040;
  --edge: #322B54;
  --edge-soft: #262046;

  --cue: #FFB03A;
  --cue-dim: #7A5316;
  --signal: #5BE7C4;
  --signal-dim: #1F5F52;
  --hot: #FF5C7A;

  --paper: #EFEAFA;
  --muted: #9A92BC;
  --muted-dim: #6B6390;

  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 20px;
  /* Aliases kept for a few components that referenced the older token names.
     Mapping them here fixes borders/radii that silently dropped out when the
     variable was undefined. */
  --radius-md: var(--radius);
  --line: var(--edge);
  --surface-2: var(--panel-hi);

  /* Shadows carry the plum hue of the housing rather than flat black, so
     elevation reads as lit from one source instead of a grey haze. */
  --shadow: 0 12px 32px -14px rgba(9, 6, 22, 0.75);
  --shadow-lift: 0 22px 50px -18px rgba(9, 6, 22, 0.85);

  /* Spacing scale. One rhythm for the whole system so density is deliberate
     rather than a pile of one-off rem values. */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;

  --display: 'Bricolage Grotesque', 'Trebuchet MS', sans-serif;
  --body: 'Inter Tight', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --step: clamp(0.9rem, 0.85rem + 0.25vw, 1rem);
  /* A little wider than default reads more comfortably on a phone in the dark. */
  --leading: 1.55;
  /* Measure cap for prose so lines don't run past a comfortable read. */
  --measure: 60ch;

  /* Motion. Variance is the point: a hover snaps, a card lifts on a spring
     with a hint of overshoot, an exit eases away. One curve for everything is
     the tell of a templated build. */
  --dur-fast: 130ms;
  --dur-mid: 240ms;
  --dur-slow: 420ms;
  --ease-out: cubic-bezier(0.33, 1, 0.68, 1);      /* quick UI response */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* entrances, lifts */
  --ease-in: cubic-bezier(0.55, 0, 0.85, 0.35);     /* exits */
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  font-family: var(--body);
  font-size: var(--step);
  line-height: var(--leading);
  color: var(--paper);
  background:
    radial-gradient(ellipse 90% 55% at 50% -10%, #2A2049 0%, transparent 70%),
    var(--ink);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* The display face is a variable font with an optical-size axis; let it adapt
   so large headings tighten and small ones stay open. */
h1, h2, h3, .strip__song, .show-card__name, .topbar__title {
  font-optical-sizing: auto;
}

/* Anything that shows changing numbers uses lining tabular figures so digits
   don't reflow as counts tick up live. */
.mono, .strip__count, .chip, .segmented .count, .show-card__stats,
.joinbox__code, .eyebrow {
  font-variant-numeric: tabular-nums lining-nums;
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.08;
  margin: 0;
  text-wrap: balance;
}

/* Tracking scales with size: the bigger the type, the tighter it wants to be.
   A single global -0.02em leaves large display looking loose and small
   headings looking cramped. */
h1 { font-size: clamp(1.85rem, 1.3rem + 2.4vw, 2.9rem); letter-spacing: -0.035em; }
h2 { font-size: clamp(1.2rem, 1.05rem + 0.7vw, 1.5rem); letter-spacing: -0.02em; }
h3 { font-size: 1.05rem; letter-spacing: -0.01em; font-weight: 600; }

p { text-wrap: pretty; }

a { color: var(--cue); text-decoration-thickness: 1px; text-underline-offset: 3px; }

:focus-visible {
  outline: 2px solid var(--cue);
  outline-offset: 2px;
  border-radius: 4px;
}

.shell {
  width: min(100% - 2rem, 62rem);
  margin-inline: auto;
  /* viewport-fit=cover deliberately extends the page under the status bar and
     notch, so the top inset has to be paid back here or the first element
     gets clipped by the system UI. */
  padding-block: max(1.5rem, env(safe-area-inset-top)) 6rem;
}

.shell--narrow { width: min(100% - 2rem, 30rem); }

.stack > * + * { margin-top: var(--gap, 1rem); }
.muted { color: var(--muted); }
.tiny { font-size: 0.8rem; }
.mono { font-family: var(--mono); }
.center { text-align: center; }
.row { display: flex; gap: 0.6rem; align-items: center; }
.row--wrap { flex-wrap: wrap; }
.row--between { justify-content: space-between; }
.push { margin-left: auto; }

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

/* ------------------------------------------------------------- eyebrow */

.eyebrow {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted-dim);
}

/* ------------------------------------------------------------- surfaces */

.panel {
  background: linear-gradient(180deg, var(--panel-hi), var(--panel));
  border: 1px solid var(--edge-soft);
  border-radius: var(--radius-lg);
  /* Containers breathe; the bottom carries a hair more than the top so the
     block sits optically centred rather than top-heavy. */
  padding: var(--s-5) var(--s-5) calc(var(--s-5) + var(--s-1));
  box-shadow: var(--shadow);
}

.panel--flush { padding: 0; overflow: hidden; }

/* ------------------------------------------------------------- forms */

label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--muted);
  margin-bottom: 0.35rem;
}

input[type='text'],
input[type='password'],
input[type='date'],
input[type='search'],
textarea,
select {
  width: 100%;
  /* 16px minimum stops iOS Safari zooming the viewport on focus. */
  font: inherit;
  font-size: max(1rem, var(--step));
  color: var(--paper);
  background: var(--ink-deep);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 0.75rem 0.9rem;
  min-height: 3rem;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-mid) var(--ease-out);
}

input::placeholder, textarea::placeholder { color: var(--muted-dim); }

input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--cue);
  box-shadow: 0 0 0 3px rgba(255, 176, 58, 0.16);
}

textarea { min-height: 6rem; resize: vertical; }

.field + .field { margin-top: var(--s-4); }

.field-error {
  color: var(--hot);
  font-size: 0.8rem;
  margin-top: 0.35rem;
  min-height: 1.1em;
}

/* min-height above reserves a line so the form doesn't jump when an error
   appears. When there's no error that reserved line is just dead space above
   the submit button, so collapse it while it's empty. */
.field-error:empty {
  min-height: 0;
  margin-top: 0;
}

/* ------------------------------------------------------------- buttons */

.btn {
  --btn-bg: var(--panel-hi);
  --btn-fg: var(--paper);
  --btn-edge: var(--edge);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  font: inherit;
  font-weight: 600;
  color: var(--btn-fg);
  background: var(--btn-bg);
  border: 1px solid var(--btn-edge);
  border-radius: var(--radius);
  /* 44px keeps every control inside the iOS touch-target guideline. */
  min-height: 2.75rem;
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  /* Filter/background respond instantly on hover; the transform settles on a
     spring so a press feels physical rather than linear. */
  transition: transform var(--dur-mid) var(--ease-spring),
              filter var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
  white-space: nowrap;
}

.btn:hover:not(:disabled) { filter: brightness(1.12); transform: translateY(-1px); }
.btn:active:not(:disabled) { transform: translateY(1px) scale(0.99); transition-duration: var(--dur-fast); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* The two accent buttons cast a faint tinted glow on hover so the primary
   action reads as lit, not just recoloured. */
.btn--primary:hover:not(:disabled) { box-shadow: 0 8px 22px -10px rgba(255, 176, 58, 0.55); }
.btn--go:hover:not(:disabled) { box-shadow: 0 8px 22px -10px rgba(91, 231, 196, 0.5); }

.btn--primary {
  --btn-bg: linear-gradient(160deg, #FFC96B, var(--cue));
  --btn-fg: #211603;
  --btn-edge: transparent;
}

.btn--go {
  --btn-bg: linear-gradient(160deg, #7DF0D6, var(--signal));
  --btn-fg: #06251E;
  --btn-edge: transparent;
}

.btn--ghost { --btn-bg: transparent; }
.btn--danger { --btn-bg: transparent; --btn-fg: var(--hot); --btn-edge: #5B2436; }
.btn--block { width: 100%; }
.btn--sm { min-height: 2.25rem; padding: 0.35rem 0.75rem; font-size: 0.85rem; }

.btn[aria-busy='true'] { color: transparent; position: relative; }
.btn[aria-busy='true']::after {
  content: '';
  position: absolute;
  inset: 50% auto auto 50%;
  width: 1rem; height: 1rem;
  margin: -0.5rem 0 0 -0.5rem;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--paper);
  animation: spin 0.7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------- chips */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.22rem 0.55rem;
  border-radius: 999px;
  border: 1px solid var(--edge);
  color: var(--muted);
  background: var(--ink-deep);
}

.chip--new     { color: var(--cue);    border-color: var(--cue-dim); }
.chip--queued  { color: var(--signal); border-color: var(--signal-dim); }
.chip--played  { color: var(--muted-dim); }
.chip--hidden  { color: var(--hot);    border-color: #5B2436; }
.chip--declined { color: var(--hot);   border-color: #5B2436; }
.chip--open    { color: var(--signal); border-color: var(--signal-dim); }
.chip--closed  { color: var(--muted-dim); }

.chip--live::before {
  content: '';
  width: 0.4rem; height: 0.4rem;
  border-radius: 50%;
  background: var(--signal);
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse { 50% { opacity: 0.25; } }

/* ============================================================= SIGNATURE
   The channel strip. Each request is a mixer channel: a segmented level
   meter shows how many people asked for it, so the DJ reads demand the way
   they read a VU meter rather than parsing a number in a table cell.
   ========================================================================= */

.strip {
  display: grid;
  grid-template-columns: auto 1fr auto;
  /* Tight column gap binds meter + title + count into one readable cluster;
     rows stay dense on purpose — this is the data surface, not the marketing. */
  gap: 0.3rem 0.85rem;
  align-items: center;
  padding: 0.8rem var(--s-4);
  border-bottom: 1px solid var(--edge-soft);
  transition: background var(--dur-fast) var(--ease-out);
}

.strip:last-child { border-bottom: 0; }
.strip:hover { background: rgba(255, 255, 255, 0.02); }
.strip[data-status='played'] { opacity: 0.5; }
.strip[data-status='played'] .strip__song { text-decoration: line-through; }
.strip[data-status='queued'] { background: rgba(91, 231, 196, 0.05); }

/* Declined: the song and meter recede, but the reason tag stays at full
   strength because that's the part a guest actually needs to read. Not struck
   through — that treatment means "played", and the two shouldn't be confused. */
.strip[data-status='declined'] .strip__lead,
.strip[data-status='declined'] .strip__song,
.strip[data-status='declined'] .strip__meta,
.strip[data-status='declined'] .strip__count { opacity: 0.5; }

.decline-tag {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.4rem;
  padding: 0.18rem 0.5rem 0.18rem 0.45rem;
  border-radius: 6px;
  background: rgba(255, 92, 122, 0.08);
  border: 1px solid rgba(255, 92, 122, 0.22);
  font-size: 0.75rem;
  line-height: 1.3;
}

.decline-tag__label {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--hot);
}

.decline-tag__reason {
  color: var(--paper);
  font-weight: 500;
}

.decline-tag__reason::before {
  content: '·';
  margin-right: 0.4rem;
  color: var(--muted-dim);
}

.strip__meter {
  display: flex;
  flex-direction: column-reverse;
  gap: 2px;
  width: 0.6rem;
  height: 2.75rem;
  grid-row: span 2;
}

.strip__seg {
  flex: 1;
  border-radius: 1px;
  background: var(--edge);
  transition: background 0.3s ease;
}

.strip__seg[data-lit='1'] { background: var(--cue); }
.strip__seg[data-lit='1']:nth-child(n + 5) { background: var(--signal); }

/* A segment that just turned on flares brighter and briefly scales up, so a
   vote landing reads as the meter climbing rather than a silent recolour. */
.strip__seg[data-rising='1'] {
  animation: seg-rise 0.5s ease;
  transform-origin: bottom center;
}

@keyframes seg-rise {
  0% { transform: scaleY(0.2); filter: brightness(2.2); }
  60% { transform: scaleY(1); filter: brightness(1.8); }
  100% { filter: brightness(1); }
}

@media (prefers-reduced-motion: reduce) {
  .strip__seg[data-rising='1'] { animation: none; }
}

/* Holds the meter (or the admin checkbox) plus optional artwork, so the strip
   stays a three-column grid whether or not a request has a cover. */
.strip__lead {
  grid-column: 1;
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.strip__body { min-width: 0; grid-column: 2; }

.strip__song {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: -0.015em;
  line-height: 1.22;
  overflow-wrap: anywhere;
  text-wrap: pretty;
}

.strip__meta {
  font-size: 0.82rem;
  line-height: 1.35;
  color: var(--muted);
  margin-top: 0.15rem;
  overflow-wrap: anywhere;
}

/* The requester name sits inside the meta line; giving it a touch more weight
   and the paper colour makes "who asked" scannable without a second line. */
.strip__meta b { font-weight: 600; color: var(--paper); font-style: normal; }

.strip__side {
  grid-column: 3;
  grid-row: span 2;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.strip__count {
  font-family: var(--mono);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--cue);
  min-width: 1.5rem;
  text-align: right;
}

.strip__actions {
  grid-column: 2 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

/* A newly arrived request drops in on a spring and eases its tint back to
   normal, so it announces itself without a hard flash. */
.strip.is-entering { animation: strip-in var(--dur-slow) var(--ease-spring) both; }

@keyframes strip-in {
  0% { opacity: 0; transform: translateY(-10px) scale(0.98); background: rgba(255, 176, 58, 0.12); }
  60% { background: rgba(255, 176, 58, 0.12); }
  100% { opacity: 1; transform: none; background: transparent; }
}

.strip.is-bumped .strip__count { animation: bump var(--dur-slow) var(--ease-spring); }

@keyframes bump {
  40% { transform: scale(1.4); color: var(--signal); }
}

/* ------------------------------------------------------------- toasts */

.toasts {
  position: fixed;
  left: 50%;
  bottom: max(1rem, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: min(100% - 2rem, 26rem);
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.75rem 0.9rem;
  border-radius: var(--radius);
  background: var(--panel-hi);
  border: 1px solid var(--edge);
  border-left: 3px solid var(--cue);
  box-shadow: var(--shadow-lift);
  font-size: 0.9rem;
  animation: toast-in var(--dur-mid) var(--ease-spring) both;
}

.toast--good { border-left-color: var(--signal); }
.toast--bad { border-left-color: var(--hot); }
.toast.is-leaving { animation: toast-out var(--dur-fast) var(--ease-in) both; }

@keyframes toast-in { from { opacity: 0; transform: translateY(14px) scale(0.96); } }
@keyframes toast-out { to { opacity: 0; transform: translateY(6px) scale(0.98); } }

/* ------------------------------------------------------------- empty */

.empty {
  padding: var(--s-7) var(--s-5) calc(var(--s-7) + var(--s-2));
  text-align: center;
  color: var(--muted);
}

.empty h3 {
  color: var(--paper);
  font-size: 1.15rem;
  margin-bottom: var(--s-2);
}

/* Cap the measure and centre it so the helper sentence reads as one calm line
   or two, never a full-width smear. */
.empty p {
  max-width: 34ch;
  margin-inline: auto;
  line-height: 1.5;
}

/* ------------------------------------------------------------- misc */

.divider { height: 1px; background: var(--edge-soft); border: 0; margin: var(--s-5) 0; }

.brand {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.brand img { height: 2.25rem; width: auto; display: block; }

.site-footer {
  border-top: 1px solid var(--edge-soft);
  margin-top: 3rem;
  padding: 1.25rem 0 max(1.25rem, env(safe-area-inset-bottom));
  text-align: center;
  font-size: 0.8rem;
  color: var(--muted-dim);
}

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -3rem;
  padding: 0.5rem 0.9rem;
  background: var(--cue);
  color: #211603;
  border-radius: var(--radius-sm);
  z-index: 100;
  transition: top 0.15s ease;
}

.skip-link:focus { top: 0.5rem; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------- autocomplete + artwork */

/* Visually hidden but still announced. The combobox uses this for its
   live-region result count. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.ac { position: relative; }

.ac-list {
  position: absolute;
  z-index: 40;
  inset-inline: 0;
  top: calc(100% + 0.35rem);
  max-height: 17rem;
  overflow-y: auto;
  margin: 0;
  padding: 0.3rem;
  list-style: none;
  background: var(--panel-hi);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  -webkit-overflow-scrolling: touch;
}

.ac-option {
  display: flex;
  gap: 0.7rem;
  align-items: center;
  /* Comfortably past the 44px touch target minimum. */
  min-height: 3rem;
  padding: 0.45rem 0.55rem;
  border-radius: calc(var(--radius) - 0.25rem);
  cursor: pointer;
}

.ac-option[aria-selected='true'] {
  background: rgba(255, 176, 58, 0.14);
  outline: 1px solid var(--cue-dim, rgba(255, 176, 58, 0.4));
}

@media (hover: hover) {
  .ac-option:hover { background: rgba(255, 255, 255, 0.05); }
}

.ac-art {
  width: 2.6rem;
  height: 2.6rem;
  flex: none;
  object-fit: cover;
  border-radius: 0.4rem;
  background: rgba(255, 255, 255, 0.05);
}

.ac-text { display: grid; min-width: 0; }

.ac-song {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

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

/* Artwork on a request row. Sized to match the meter beside it. */
.strip__art {
  width: 2.4rem;
  height: 2.4rem;
  flex: none;
  object-fit: cover;
  border-radius: 0.35rem;
  background: rgba(255, 255, 255, 0.05);
}

/* Autocomplete pending + empty states */

.ac-pending,
.ac-empty {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 3rem;
  padding: 0.45rem 0.7rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.ac-spinner {
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  border: 2px solid var(--edge);
  border-top-color: var(--cue);
  border-radius: 50%;
  animation: ac-spin 0.6s linear infinite;
}

@keyframes ac-spin {
  to { transform: rotate(360deg); }
}

/* Respect a reduced-motion preference: pulse the opacity instead of spinning. */
@media (prefers-reduced-motion: reduce) {
  .ac-spinner {
    animation: ac-pulse 1s ease-in-out infinite;
  }
  @keyframes ac-pulse {
    0%, 100% { opacity: 0.3; }
    50% { opacity: 1; }
  }
}
