:root {
  --bg: #020617;
  --panel: #0f172a;
  --panel2: #1e293b;
  --text: #f8fafc;
  --accent: #22c55e;
  --danger: #ef4444;
  --gold: #fbbf24;
}

* { box-sizing: border-box; }

/* ── Consistent scrollbars across browsers ── */
/* Chrome / Safari / Edge */
::-webkit-scrollbar          { width: 4px; height: 4px; }
::-webkit-scrollbar-track    { background: transparent; }
::-webkit-scrollbar-thumb    { background: rgba(255,255,255,0.18); border-radius: 2px; }
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.35); }
/* Firefox */
* { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.18) transparent; }

/* ── Idle sleep ──
   Added to <body> by ui.js after 5 minutes of no user interaction to halt
   every running CSS animation (flames, idle emoji bob, title glow, winner
   glow, any in-flight confetti). The idle-rotation canvas loop and confetti
   interval are stopped separately in JS. Removed on the next interaction.
   Only affects `animation`, not `transition`, so the sidebar slide and fades
   still work while asleep. */
body.anim-paused *,
body.anim-paused *::before,
body.anim-paused *::after { animation-play-state: paused !important; }

/* ── Reduced effects ──
   Global, device-level toggle (Features menu) for low-end GPUs — notably
   Firefox on Windows, where backdrop-filter, filter: blur(), mix-blend-mode,
   and per-frame drop-shadow re-rasterization are far costlier than on macOS.
   Because the idle rotation redraws the canvas every frame, these effects
   recomposite continuously. This strips them while keeping the layout and the
   cheap (transform/opacity) animations like the wheel spin and flame flicker. */
body.reduced-effects .panel {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
/* Flames: drop blur + blend, and freeze the flicker to a static gradient.
   The scaleY keyframes force the (likely un-layerized) multi-gradient fill to
   repaint every frame on weak GPUs — and they flicker *faster* during a spin,
   so making them static helps full-speed spins too, not just idle. */
body.reduced-effects .wheel-wrap::before,
body.reduced-effects .wheel-wrap::after {
  filter: none;
  mix-blend-mode: normal;
  animation: none;
}
/* Drop shadows that get re-rasterized every frame (the canvas redraws each
   idle frame; the emoji/pointer animate). */
body.reduced-effects canvas,
body.reduced-effects .wheel-emoji,
body.reduced-effects .pointer { filter: none; }
/* Stop the continuous shadow-glow animations — static base shadows remain. */
body.reduced-effects .wheel-title-text.visible { animation: none; }
body.reduced-effects .winner-heart-wrap { animation: winnerPop 0.7s ease !important; }

body {
  margin: 0;
  padding: 20px;
  overflow: hidden;
  min-height: 100vh;
  font-family: Arial, Helvetica, sans-serif;
  color: var(--text);
  background: radial-gradient(circle at top, #1e293b, #020617);
}

.app {
  width: 100%;
  max-width: 1800px;
  height: calc(100vh - 40px);
  margin: auto;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: minmax(0, 1fr);
  column-gap: 24px;
  transition: column-gap 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
}
.app.sidebar-collapsed { column-gap: 0; }

.sidebar-wrapper {
  width: 520px;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden; /* always clip — panel stays 520px, only the curtain moves */
  transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.sidebar-wrapper > .panel {
  flex: 1;
  /* Fixed width prevents the panel from reflowing as the wrapper animates.
     The wrapper clips it; the panel content never word-wraps mid-transition. */
  width: 520px;
  flex-shrink: 0;
  min-height: 0;
}

.panel {
  background: rgba(15,23,42,0.94);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 24px;
  padding: 14px;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 10px 30px rgba(0,0,0,0.45);
  overflow: hidden;
}

.sidebar { display: flex; flex-direction: column; min-height: 0; position: relative; }
h1 { margin-top: 0; margin-bottom: 4px; font-size: 28px; text-align: center; }
h2 { margin-top: 0; margin-bottom: 6px; font-size: 14px; opacity: 0.75; text-transform: uppercase; letter-spacing: 1px; }
h3 { margin-top: 0; margin-bottom: 6px; font-size: 14px; opacity: 0.75; text-transform: uppercase; letter-spacing: 1px; }
.subtitle {
  opacity: 0.72;
  font-size: 12px;
  line-height: 1.4;
  margin-bottom: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  text-align: center;
}

/* ── Wheel list ── */
.wheel-list-section { margin-bottom: 0; }

/* ── Section divider (wheels ↔ entrants) ── */
.section-divider {
  flex-shrink: 0;
  height: 13px;
  cursor: ns-resize;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Left and right line segments flanking the grip glyph */
.section-divider::before,
.section-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgba(255,255,255,0.1);
  transition: background 0.15s;
}
/* Grip glyph — always visible, brightens on hover/drag */
.section-divider span {
  font-size: 12px;
  color: rgba(255,255,255,0.25);
  flex-shrink: 0;
  line-height: 1;
  user-select: none;
  transition: color 0.15s;
}
.section-divider:hover::before,
.section-divider:hover::after,
body.resizing-v .section-divider::before,
body.resizing-v .section-divider::after { background: rgba(255,255,255,0.4); }
.section-divider:hover span,
body.resizing-v .section-divider span   { color: rgba(255,255,255,0.75); }
/* Keep the resize cursor everywhere while a drag is in progress */
body.resizing-v,
body.resizing-v * { cursor: ns-resize !important; user-select: none !important; }

.wheel-add-row {
  display: grid;
  grid-template-columns: 1fr 70px;
  gap: 8px;
  margin-bottom: 6px;
}

.wheel-scroll-wrap {
  position: relative;
  overflow: hidden;
  margin-bottom: 6px;
}

.wheel-list-scroll {
  /* Default height; the Wheels/Entrants divider overrides this inline. Each
     row ≈ 42px + 6px gap. min-height keeps at least ~1.5 rows visible so the
     list can never be dragged smaller than that (matches DIVIDER_MIN in ui.js). */
  height: 110px;
  min-height: 70px;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 6px;
  scroll-behavior: smooth;
  /* Reserve scrollbar gutter permanently so content width never shifts
     when the bar appears. padding-right is no longer needed. */
  scrollbar-gutter: stable;
}

.wheel-row {
  display: grid;
  grid-template-columns: 22px 1fr auto auto;
  gap: 6px;
  align-items: center;
  background: rgba(255,255,255,0.05);
  border-radius: 10px;
  padding: 4px 8px;
  border: 1px solid transparent;
  cursor: pointer;
  /* Reserve border-top/bottom space so drag indicators don't shift layout */
  border-top: 2px solid transparent;
  border-bottom: 2px solid transparent;
}

.wheel-row.active-wheel {
  background: rgba(251,191,36,0.1);
  border-color: rgba(251,191,36,0.3);
}

.wheel-row.dragging { opacity: 0.35; }
.wheel-row.drag-over-top    { border-top:    2px solid rgba(251,191,36,0.8); }
.wheel-row.drag-over-bottom { border-bottom: 2px solid rgba(251,191,36,0.8); }

.wheel-drag-handle,
.entrant-drag-handle {
  cursor: grab;
  opacity: 0.45;
  font-size: 19px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
}
.wheel-drag-handle:hover,
.entrant-drag-handle:hover { opacity: 0.8; }

/* Per-wheel settings (gear) button — solid, like the entrant weight buttons.
   Uses the default button:hover translateY lift (no override). */
.wheel-options-btn {
  width: 28px; height: 28px;
  padding: 0;
  border-radius: 8px;
  background: #475569;
  color: white;
}

.wheel-add-inline {
  text-align: center;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: var(--accent);
  background: rgba(34,197,94,0.10);
  border: 1px dashed rgba(34,197,94,0.5);
  cursor: pointer;
  padding: 6px 8px;
  margin-bottom: 6px; /* separates the pinned button from the scrolling list below */
  border-radius: 8px;
  user-select: none;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.wheel-add-inline:hover {
  color: #fff;
  background: rgba(34,197,94,0.28);
  border-color: rgba(34,197,94,0.85);
}

.wheel-title-edit,
.entrant-name-edit {
  background: transparent !important;
  border: none !important;
  border-bottom: 1px solid rgba(255,255,255,0.2) !important;
  border-radius: 0 !important;
  padding: 4px 6px !important;
  color: white;
  font-size: 14px;
  width: 100%;
  transition: border-color 0.15s;
}
.wheel-title-edit:focus,
.entrant-name-edit:focus {
  outline: none;
  border-bottom-color: var(--gold) !important;
}

.clone-btn {
  width: 30px; height: 30px;
  padding: 0;
  border-radius: 8px;
  background: #475569;
  color: white;
  font-size: 14px;
}

/* ── Lucide icon helpers ── */
/* Icon-only buttons (bulk-btn, mute, history, settings) */
.btn-icon {
  width: 18px;
  height: 18px;
  pointer-events: none;
  flex-shrink: 0;
}
/* Groups icon + text on one line inside a column-flex label */
.label-text {
  display: flex;
  align-items: center;
  gap: 5px;
}

/* Inline decorative icons in labels and section headings */
.label-icon {
  width: 14px;
  height: 14px;
  vertical-align: middle;
  margin-right: 3px;
  flex-shrink: 0;
  pointer-events: none;
}
/* Heading icon in h3 (Mischief Settings) */
.heading-icon {
  width: 16px;
  height: 16px;
  vertical-align: middle;
  pointer-events: none;
}

/* ── Rest of sidebar ── */
.input-row {
  display: grid;
  grid-template-columns: 1fr 70px;
  gap: 8px;
  margin-bottom: 8px;
}

input {
  width: 100%;
  border: none;
  border-radius: 10px;
  padding: 8px 10px;
  background: var(--panel2);
  color: white;
  font-size: 14px;
}

button {
  border: none;
  border-radius: 10px;
  padding: 8px 10px;
  cursor: pointer;
  font-weight: bold;
  transition: 0.15s ease;
  font-size: 14px;
  /* Center icon or text content in every button */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

button:hover { transform: translateY(-1px); }
.primary { background: var(--accent); color: black; }
.danger  { background: var(--danger);  color: white; }

.entrant-section {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  position: relative;
}

/* Add-new input pinned at the bottom of .entrant-section */
.entrant-add-input {
  flex-shrink: 0;
  margin-bottom: 6px; /* separates the pinned input from the scrolling list below */
  background: rgba(34,197,94,0.08) !important; /* green tint; overrides global input rule */
  border: 1px dashed rgba(34,197,94,0.5);
  border-radius: 10px;
  padding: 9px 12px;
  color: white;
  font-size: 14px;
  width: 100%;
  transition: border-color 0.15s, background 0.15s, border-style 0.15s;
}
.entrant-add-input:focus {
  outline: none;
  background: rgba(34,197,94,0.14) !important;
  border-color: var(--accent);
  border-style: solid;
}
.entrant-add-input::placeholder { color: var(--accent); opacity: 0.6; }

.entrant-scroll-wrap {
  position: relative;
  flex: 1;
  min-height: 72px; /* keep at least ~1.5 entrant rows visible */
  overflow: hidden;
}

.entrant-list {
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 6px;
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
}

.scroll-fade {
  position: absolute;
  left: 0; right: 0;
  height: 36px;
  z-index: 20;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.scroll-fade.show { opacity: 1; }
.scroll-fade.top    { top: 0;    background: linear-gradient(to bottom, rgba(15,23,42,1), rgba(15,23,42,0)); }
.scroll-fade.bottom { bottom: 0; background: linear-gradient(to top,   rgba(15,23,42,1), rgba(15,23,42,0)); }

.scroll-indicator {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-size: 18px;
  color: rgba(255,255,255,0.75);
}
.scroll-fade.top    .scroll-indicator { top: 4px; }
.scroll-fade.bottom .scroll-indicator { bottom: 4px; }

.entrant {
  display: grid;
  grid-template-columns: 22px minmax(0,1fr) auto auto 32px;
  gap: 8px;
  align-items: center;
  background: rgba(255,255,255,0.05);
  border-radius: 10px;
  padding: 6px 8px;
  min-height: 44px;
  /* Reserve border-top/bottom space so drag indicators don't shift layout */
  border-top: 2px solid transparent;
  border-bottom: 2px solid transparent;
}
.entrant.dragging { opacity: 0.35; }
.entrant.drag-over-top    { border-top:    2px solid rgba(251,191,36,0.8); }
.entrant.drag-over-bottom { border-bottom: 2px solid rgba(251,191,36,0.8); }

.weight-control { display: flex; align-items: center; gap: 4px; }

.weight-btn {
  width: 28px; height: 28px;
  border-radius: 8px;
  padding: 0;
  background: #475569;
  color: white;
  font-size: 16px;
}

.weight-input {
  width: 38px; /* fits up to 3 digits */
  text-align: center;
  font-weight: bold;
  font-size: 15px;
  padding: 4px 2px;
  -moz-appearance: textfield;
  appearance: textfield;
}
.weight-input::-webkit-outer-spin-button,
.weight-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.remove-btn {
  width: 28px; height: 28px;
  padding: 0;
  border-radius: 9px;
  background: var(--danger);
  color: white;
}

/* ── Paused entrant (temporarily off the wheel) ── */
.entrant.paused .entrant-name-edit {
  text-decoration: line-through;
  opacity: 0.5;
}
/* Pause button lights up while the entrant is paused */
.weight-btn.pause-btn.paused-on {
  background: var(--accent);
}
.weight-btn.pause-btn.paused-on .btn-icon { filter: brightness(0); } /* dark icon on accent */
.stats-muted { opacity: 0.6; }

/* Entry totals — the last item inside the scrollable entrant list, so it sits
   directly below the last entrant and scrolls with the content. */
.stats {
  flex-shrink: 0;
  opacity: 0.7;
  font-size: 13px;
  text-align: center;
  padding-top: 2px;
}

/* ── Sidebar footer ── */
.sidebar-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 5px;
  margin-top: 8px;
  flex-shrink: 0;
  border-top: 1px solid rgba(255,255,255,0.08); /* divider just above the settings button */
}

/* ── Entrant controls row ── */
.entrant-controls {
  display: flex;
  gap: 6px;
  margin-bottom: 8px;
  align-items: stretch;
}
.bulk-btn {
  flex: 1;
  background: #475569;
  color: white;
  border-radius: 10px;
  padding: 8px 10px;
}
.clear-right-btn {
  width: 33%;
  flex-shrink: 0;
}

/* ── Wheel button row ── */
.wheel-clear-row {
  display: flex;
  gap: 6px;
  margin-bottom: 6px;
  align-items: stretch;
}

/* ── Panel toggle button ── */
.panel-toggle {
  position: absolute;
  top: 20px;
  left: 20px;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  z-index: 100;
  background: none;
  color: rgba(255,255,255,0.2);
  border: none;
  font-size: 18px;
  font-weight: 300;
  padding: 0;
  line-height: 1;
  transition: color 0.15s;
}
.panel-toggle:hover {
  background: none;
  color: rgba(255,255,255,0.55);
  transform: none;
}

/* ── Wheel panel ── */
.wheel-wrap {
  position: relative;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* Pink fire that rises from the bottom of the wheel panel.
   The animation duration is driven by --flame-speed, set from JS to mirror
   the wheel's angular velocity (faster spin → faster flicker). */
.wheel-wrap::before,
.wheel-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  transform-origin: 50% 100%;
  /* Safari bug: overflow:hidden + border-radius on .wheel-wrap fails to
     clip pseudo-elements that have CSS filter or mix-blend-mode. Giving
     these layers the parent's border-radius makes them self-clip, so the
     flame can't poke out through the rounded bottom corners on Safari. */
  border-radius: inherit;
}
.wheel-wrap::before {
  background:
    radial-gradient(ellipse 35% 72% at 22% 105%, rgba(255, 45,120,0.90) 0%, transparent 72%),
    radial-gradient(ellipse 42% 84% at 50% 108%, rgba(255, 30, 90,0.95) 0%, transparent 74%),
    radial-gradient(ellipse 35% 72% at 78% 105%, rgba(255, 45,120,0.90) 0%, transparent 72%),
    linear-gradient(to top, rgba(255,45,120,0.45) 0%, rgba(255,105,180,0.18) 32%, transparent 60%);
  filter: blur(7px);
  animation: flameFlicker var(--flame-speed, 1.6s) ease-in-out infinite;
}
.wheel-wrap::after {
  background:
    radial-gradient(ellipse 22% 62% at 35% 105%, rgba(255,210,225,0.85) 0%, transparent 70%),
    radial-gradient(ellipse 26% 67% at 50% 108%, rgba(255,170,200,0.90) 0%, transparent 73%),
    radial-gradient(ellipse 22% 62% at 65% 105%, rgba(255,210,225,0.85) 0%, transparent 70%);
  filter: blur(9px);
  mix-blend-mode: screen;
  animation: flameFlicker2 var(--flame-speed, 1.6s) ease-in-out infinite alternate;
}

/* Freeze flames while a winner is on screen; resumes when the user
   dismisses the winner and the wheel returns to idle. */
.wheel-wrap.flames-paused::before,
.wheel-wrap.flames-paused::after {
  animation-play-state: paused;
}

/* Only scaleY: keeps the side edges fixed (no horizontal pulsation gaps)
   while letting the top edge lick up and down like fire. transform-origin
   is 50% 100% on the parent, so all the motion happens off the top. */
@keyframes flameFlicker {
  0%, 100% { transform: scaleY(1.00); }
  15%      { transform: scaleY(1.18); }
  30%      { transform: scaleY(0.90); }
  45%      { transform: scaleY(1.28); }
  60%      { transform: scaleY(0.93); }
  78%      { transform: scaleY(1.12); }
}
@keyframes flameFlicker2 {
  0%   { transform: scaleY(0.95); }
  18%  { transform: scaleY(1.22); }
  36%  { transform: scaleY(0.85); }
  55%  { transform: scaleY(1.30); }
  72%  { transform: scaleY(0.95); }
  100% { transform: scaleY(1.15); }
}

canvas {
  width: 100%;
  height: 100%;
  display: block;
  cursor: pointer;
  filter: drop-shadow(0 0 24px rgba(0,0,0,0.5));
  position: relative;
  z-index: 5;
}

/* ── Wheel title overlay ── */
.wheel-title {
  position: absolute;
  top: 18px;
  left: 0; right: 0;
  text-align: center;
  z-index: 25;
  pointer-events: none;
  padding: 0 24px;
}

.wheel-title-text {
  display: inline-block;
  /* font-size is dynamically set by fitWheelTitle() in JS — kept here as
     the initial paint value before the first JS measurement runs. */
  font-size: 72px;
  font-weight: 900;
  letter-spacing: 3px;
  text-transform: uppercase;
  color: #ff2d78;
  text-shadow:
    0 0 10px rgba(255,45,120,1),
    0 0 30px rgba(255,45,120,0.85),
    0 0 60px rgba(255,45,120,0.6),
    0 0 100px rgba(255,45,120,0.35),
    0 2px 8px rgba(0,0,0,0.9);
  line-height: 1.1;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.4s ease, transform 0.4s ease;
}

.wheel-title-text.visible {
  opacity: 1;
  transform: translateY(0);
  animation: pinkGlowPulse 1.8s ease-in-out infinite;
}

@keyframes pinkGlowPulse {
  0%, 100% {
    text-shadow:
      0 0 10px rgba(255,45,120,1),
      0 0 30px rgba(255,45,120,0.85),
      0 0 60px rgba(255,45,120,0.55),
      0 0 100px rgba(255,45,120,0.3),
      0 2px 8px rgba(0,0,0,0.9);
  }
  50% {
    text-shadow:
      0 0 18px rgba(255,45,120,1),
      0 0 55px rgba(255,45,120,1),
      0 0 110px rgba(255,45,120,0.8),
      0 0 180px rgba(255,45,120,0.5),
      0 2px 8px rgba(0,0,0,0.9);
  }
}

/* Pointer: left-pointing triangle, hinge on right edge */
.pointer {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  transform-origin: right center;
  width: 0; height: 0;
  border-top:    16px solid transparent;
  border-bottom: 16px solid transparent;
  border-right:  64px solid #ff2d78;
  z-index: 20;
  filter:
    drop-shadow(0 0 10px rgba(0,0,0,0.5))
    drop-shadow(0 0 8px rgba(255,45,120,0.7));
}

.pointer.tick-cw  { animation: pointerTickCW  0.11s ease; }
.pointer.tick-ccw { animation: pointerTickCCW 0.11s ease; }

@keyframes pointerTickCW {
  0%   { transform: translateY(-50%) rotate(0deg); }
  40%  { transform: translateY(-50%) rotate(-20deg); }
  100% { transform: translateY(-50%) rotate(0deg); }
}
@keyframes pointerTickCCW {
  0%   { transform: translateY(-50%) rotate(0deg); }
  40%  { transform: translateY(-50%) rotate(20deg); }
  100% { transform: translateY(-50%) rotate(0deg); }
}

.wheel-emoji {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 15;
  user-select: none;
  pointer-events: none;
  filter: drop-shadow(0 0 12px rgba(0,0,0,0.5));
  animation: emojiIdle 3.5s ease-in-out infinite;
  line-height: 1;
}
.wheel-emoji img {
  display: block;
  width: 1.4em;
  height: 1.4em;
  -webkit-user-drag: none;
}

@keyframes emojiIdle {
  0%   { transform: translate(-50%,-50%) rotate(-5deg); }
  50%  { transform: translate(-50%,-50%) rotate(5deg); }
  100% { transform: translate(-50%,-50%) rotate(-5deg); }
}

.wheel-emoji.excited  { animation: emojiExcited  0.22s linear      infinite; }
.wheel-emoji.nervous  { animation: emojiNervous  0.07s linear      infinite; }
.wheel-emoji.mischief { animation: emojiMischief 0.18s ease-in-out infinite; }
.wheel-emoji.shocked  { animation: emojiShocked  0.3s  ease-out    1; }

@keyframes emojiExcited {
  0%   { transform: translate(-50%,-50%) rotate(-10deg) scale(1); }
  50%  { transform: translate(-50%,-50%) rotate(10deg)  scale(1.08); }
  100% { transform: translate(-50%,-50%) rotate(-10deg) scale(1); }
}
@keyframes emojiNervous {
  0%   { transform: translate(-50%,-50%) translateX(-4px); }
  50%  { transform: translate(-50%,-50%) translateX(4px); }
  100% { transform: translate(-50%,-50%) translateX(-4px); }
}
@keyframes emojiMischief {
  0%   { transform: translate(-50%,-50%) rotate(-14deg) scale(1); }
  50%  { transform: translate(-50%,-50%) rotate(14deg)  scale(1.15); }
  100% { transform: translate(-50%,-50%) rotate(-14deg) scale(1); }
}
@keyframes emojiShocked {
  0%   { transform: translate(-50%,-50%) scale(1.3); }
  100% { transform: translate(-50%,-50%) scale(1); }
}

.winner-overlay {
  /* The overlay is moved to <body> at script startup (see syncOverlayToWheelPanel
     in wheel.js) so it isn't trapped inside the wheel-wrap's stacking context
     — which is created because .panel has backdrop-filter, which both
     creates a stacking context and acts as the containing block for
     fixed descendants. Body-level absolute positioning + JS-set
     left/top/width/height to match wheelWrap.getBoundingClientRect()
     keeps the popup visually over the wheel panel while letting its
     z-index (100) sit in body's root stacking context, above the
     body-level confetti (z-index 25) and below modals (200). */
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding-top: 8%;
  pointer-events: none;
  opacity: 0;
  /* Only fade opacity. A bare `0.4s ease` shorthand would also ease the
     left/top/width/height/padding that syncOverlayToWheelPanel() sets each
     frame, making the box lag behind the wheel during the sidebar animation. */
  transition: opacity 0.4s ease;
  z-index: 100;
}
.winner-overlay.show { opacity: 1; }

/* The glow lives on the wrapper, NOT on winner-box itself.
   Reason: filter applied to an element is composited BEFORE clip-path in
   browsers, so drop-shadow on the clipped element gets clipped away.
   Moving filter to the parent means the parent sees the already-clipped
   heart shape and casts the shadow outward correctly. */
.winner-heart-wrap {
  position: relative;
  animation: winnerPop 0.7s ease;
}
.winner-heart-wrap:not(:has(.winner-box.exploded)) {
  animation: winnerPop 0.7s ease, heartGlow 5s linear infinite;
}

@keyframes heartGlow {
  0%   { filter: drop-shadow(0 0 14px hsla(  0,100%,65%,1)) drop-shadow(0 0 40px hsla(  0,100%,65%,0.6)) drop-shadow(0 0 80px hsla(  0,100%,65%,0.3)); }
  17%  { filter: drop-shadow(0 0 14px hsla( 60,100%,65%,1)) drop-shadow(0 0 40px hsla( 60,100%,65%,0.6)) drop-shadow(0 0 80px hsla( 60,100%,65%,0.3)); }
  33%  { filter: drop-shadow(0 0 14px hsla(120,100%,65%,1)) drop-shadow(0 0 40px hsla(120,100%,65%,0.6)) drop-shadow(0 0 80px hsla(120,100%,65%,0.3)); }
  50%  { filter: drop-shadow(0 0 14px hsla(180,100%,65%,1)) drop-shadow(0 0 40px hsla(180,100%,65%,0.6)) drop-shadow(0 0 80px hsla(180,100%,65%,0.3)); }
  67%  { filter: drop-shadow(0 0 14px hsla(240,100%,65%,1)) drop-shadow(0 0 40px hsla(240,100%,65%,0.6)) drop-shadow(0 0 80px hsla(240,100%,65%,0.3)); }
  83%  { filter: drop-shadow(0 0 14px hsla(300,100%,65%,1)) drop-shadow(0 0 40px hsla(300,100%,65%,0.6)) drop-shadow(0 0 80px hsla(300,100%,65%,0.3)); }
  100% { filter: drop-shadow(0 0 14px hsla(360,100%,65%,1)) drop-shadow(0 0 40px hsla(360,100%,65%,0.6)) drop-shadow(0 0 80px hsla(360,100%,65%,0.3)); }
}

.winner-box {
  text-align: center;
  width: 560px;
  height: 420px;
  box-sizing: border-box;
  padding: 61px 70px 89px 70px;
  background: rgba(210, 25, 100, 0.75);
  /* backdrop-filter omitted: when the parent has filter it creates an
     isolated rendering group, making backdrop-filter blur transparent
     pixels rather than the wheel behind — so it has no useful effect. */
  clip-path: url(#heart-clip);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
/* Exploded / nobody-wins state: revert to a plain dark rectangle. */
.winner-box.exploded {
  clip-path: none;
  border-radius: 20px;
  border: 2px solid rgba(239,68,68,0.4);
  width: auto;
  height: auto;
  padding: 28px 44px;
  background: rgba(0,0,0,0.78);
  box-shadow: 0 0 40px rgba(239,68,68,0.6), 0 0 90px rgba(239,68,68,0.35);
}

/* Hide the "🎉 WINNER 🎉" label inside the heart — the winner name
   alone fills the space. Keep it visible for the exploded state. */
.winner-box:not(.exploded) .winner-title {
  display: none;
}
.winner-title {
  font-size: 28px;
  font-weight: 900;
  color: var(--gold);
  letter-spacing: 2px;
  margin-bottom: 10px;
}
.winner-title.bad { color: var(--danger); }

.winner-name {
  font-size: 52px;
  line-height: 1.1;
  font-weight: 900;
  text-transform: uppercase;
  max-width: 360px;
  word-break: break-word;
  color: #ffffff;
}

@keyframes winnerPop {
  0%   { transform: scale(0.5) rotate(-8deg); opacity: 0; }
  70%  { transform: scale(1.08) rotate(2deg); }
  100% { transform: scale(1)    rotate(0deg); opacity: 1; }
}

.mute-btn {
  position: absolute;
  bottom: 16px;
  right: 16px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(15,23,42,0.72);
  color: rgba(255,255,255,0.55);
  border: 1px solid rgba(255,255,255,0.1);
  font-size: 18px;
  padding: 0;
  z-index: 26;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background 0.15s, color 0.15s;
}
.mute-btn:hover {
  background: rgba(15,23,42,0.9);
  color: rgba(255,255,255,0.85);
  transform: none;
}

/* History button: same chrome as mute, mirrored to bottom-left.
   The "off" (list hidden) look uses BOTH a grayscale filter AND a low
   opacity. Safari's color-emoji renderer sometimes ignores CSS filters,
   so the opacity is what guarantees a visibly dimmed/inactive look in
   every browser; the grayscale just adds extra desaturation where it
   does apply (Chrome). Hover only adjusts the chrome — it never touches
   opacity or filter, so the "off" look is purely tied to .active. */
.history-btn {
  position: absolute;
  bottom: 16px;
  left: 16px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(15,23,42,0.72);
  color: rgba(255,255,255,0.55);
  border: 1px solid rgba(255,255,255,0.1);
  font-size: 18px;
  padding: 0;
  z-index: 26;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0.45;
  -webkit-filter: grayscale(1);
  filter: grayscale(1);
  transition: background 0.15s, color 0.15s, opacity 0.2s,
              -webkit-filter 0.2s, filter 0.2s;
}
.history-btn:hover {
  background: rgba(15,23,42,0.9);
  color: rgba(255,255,255,0.85);
  transform: none;
}
.history-btn.active {
  opacity: 1;
  -webkit-filter: none;
  filter: none;
}

/* Winner-history list — plain text floating above the history button.
   Anchored at bottom; flex-direction: column-reverse + DOM-prepend gives
   newest-at-bottom and clips OLDEST entries off the top when overflowing. */
.history-list {
  position: absolute;
  left: 16px;
  right: 70px; /* leave space so long names don't crash into the wheel */
  bottom: 64px;
  z-index: 26;
  pointer-events: none;
  display: flex;
  flex-direction: column-reverse;
  gap: 4px;
  color: white;
  font-size: 16px;
  font-weight: 600;
  text-shadow: 0 1px 3px rgba(0,0,0,0.6), 0 0 8px rgba(0,0,0,0.4);
  max-height: calc(100% - 110px);
  overflow: hidden;
}
.history-list.hidden { display: none; }
.history-list .history-item {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.settings-btn {
  flex-shrink: 0;
  width: 40px; height: 40px;
  border-radius: 50%;
  background: rgba(15,23,42,0.95);
  color: white;
  font-size: 18px;
  border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  padding: 0;
}
.settings-btn:hover { transform: rotate(18deg); }

/* Quick bot connect/disconnect button + its status text in the sidebar footer */
.ws-quick-btn {
  flex-shrink: 0;
  width: 40px; height: 40px;
  border-radius: 50%;
  background: rgba(15,23,42,0.95);
  color: white;
  border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0 4px 16px rgba(0,0,0,0.35);
  padding: 0;
}
.ws-quick-btn.connected {
  border-color: var(--accent);
  background: rgba(34,197,94,0.18);
}
.ws-quick-btn:hover { transform: rotate(18deg); } /* tilt like the settings gear, no jump */
.sidebar-footer .ws-status {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Mischief Settings modal overlay ── */
.settings-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.55);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
}
.settings-overlay.hidden { display: none; }

.settings-panel {
  width: min(1040px, 96vw); /* fits the 3-column body */
  height: 85vh;
  background: rgba(15,23,42,0.98);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 24px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.6);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.settings-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px 12px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
}
.settings-panel-header h3 { margin: 0; font-size: 16px; opacity: 1; letter-spacing: 0; text-transform: none; display: flex; align-items: center; gap: 6px; }
/* Which wheel the Mischief menu is editing (shown right of the title) */
.settings-subtitle {
  font-size: 13px;
  opacity: 0.6;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 45%;
  text-align: right;
}
/* Compact panel for the global settings menu (auto height, scrolls if tall) */
.settings-panel-compact { height: auto; max-height: 85vh; width: min(560px, 94vw); }
.global-settings-body { padding: 16px 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 18px; }

/* ── Bot / WebSocket integration (global settings) ── */
.ws-conn-row { display: flex; align-items: center; gap: 8px; margin-top: 2px; flex-wrap: wrap; }
.ws-conn-row button { min-width: 96px; }
.ws-status { font-size: 12px; opacity: 0.7; }
.ws-status.ok  { color: var(--accent); opacity: 1; }
.ws-status.err { color: var(--danger); opacity: 1; }

.ws-perm-grid {
  display: grid;
  grid-template-columns: auto repeat(6, 1fr);
  gap: 5px 6px;
  align-items: center;
}
.ws-perm-head {
  text-align: center;
  font-size: 10px;
  opacity: 0.6;
  white-space: nowrap;
}
.ws-perm-cmd {
  font-weight: 700;
  text-transform: capitalize;
  font-size: 13px;
  padding-right: 8px;
}
.ws-perm-cell { display: flex; justify-content: center; }
.ws-perm-grid input[type="radio"] {
  width: 15px; height: 15px;
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
/* Radios used inline in feature-label rows (limit mode) */
.settings-panel label.feature-label input[type="radio"] {
  width: 16px; height: 16px;
  flex-shrink: 0;
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.settings-close-btn {
  background: none;
  border: none;
  color: rgba(255,255,255,0.45);
  font-size: 13px;
  height: 28px;
  padding: 0 10px;
  border-radius: 6px;
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
}
.settings-close-btn:hover { color: white; background: rgba(255,255,255,0.08); transform: none; }

.settings-body-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.settings-panel-body {
  height: 100%;
  overflow-y: auto;
  padding: 16px 20px;
  display: grid;
  /* 3 columns: toggles | chances | sounds+images (widest, takes the slack) */
  grid-template-columns: 200px 200px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}
/* Medium widths: drop to two columns and let sounds/images span the row below.
   (The ≤640px rule further down collapses everything to a single column.) */
@media (max-width: 900px) {
  .settings-panel-body { grid-template-columns: 200px minmax(0, 1fr); }
  .settings-panel-body > .settings-col:last-child { grid-column: 1 / -1; }
}

.settings-section { display: flex; flex-direction: column; gap: 12px; }
.settings-section-title {
  font-size: 11px;
  font-weight: 700;
  opacity: 0.5;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin: 0 0 2px;
}
.settings-panel label { display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
.settings-panel input { background: #1e293b; }
.settings-panel input[type="range"]    { padding: 0; background: transparent; }
/* Feature toggles: checkbox + text on one line */
.settings-panel label.feature-label {
  flex-direction: row;
  align-items: center;
  gap: 10px;
  cursor: pointer;
}
.settings-panel label.feature-label input[type="checkbox"] {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  cursor: pointer;
  accent-color: var(--accent);
  background: transparent;
}

.settings-panel-footer {
  display: flex;
  gap: 10px;
  padding: 12px 20px 16px;
  border-top: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
}

/* ── Image settings ── */
.img-states-grid { display: flex; flex-direction: column; gap: 8px; }
.img-state-section {
  background: rgba(255,255,255,0.03);
  border-radius: 12px;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.img-state-header { font-size: 12px; font-weight: 600; opacity: 0.8; display: flex; align-items: center; }
.img-url-list { display: flex; flex-direction: column; gap: 3px; }
.img-url-item {
  display: grid;
  grid-template-columns: 1fr 24px;
  gap: 5px;
  align-items: center;
}
.img-url-text {
  font-size: 11px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  opacity: 0.75;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.img-remove-btn {
  width: 24px; height: 24px;
  padding: 0;
  border-radius: 5px;
  background: rgba(239,68,68,0.18);
  color: #ef4444;
  font-size: 11px;
  flex-shrink: 0;
}
.img-remove-btn:hover { background: rgba(239,68,68,0.38); transform: none; }
.img-url-edit {
  background: #0f172a;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 7px;
  padding: 4px 7px;
  color: white;
  width: 100%;
  font-size: 11px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.img-url-edit:focus { outline: none; border-color: var(--accent); }
.img-add-row { display: grid; grid-template-columns: 1fr 24px 24px; gap: 5px; align-items: center; }
.img-gallery-btn {
  width: 24px; height: 24px;
  padding: 0;
  border-radius: 5px;
  background: rgba(255,255,255,0.1);
  color: white;
  flex-shrink: 0;
}
.img-gallery-btn:hover { background: rgba(255,255,255,0.22); transform: none; }
.img-url-input {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  padding: 4px 7px;
  background: #0f172a;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 7px;
  color: white;
}
.img-url-input:focus { outline: none; border-color: var(--accent); }
.img-add-btn {
  width: 24px; height: 24px;
  padding: 0;
  border-radius: 5px;
  background: rgba(34,197,94,0.18);
  color: var(--accent);
  flex-shrink: 0;
}
.img-add-btn:hover { background: rgba(34,197,94,0.38); transform: none; }
.settings-col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.snd-row {
  display: grid;
  grid-template-columns: 100px 1fr;
  gap: 8px;
  align-items: center;
}
.snd-label { font-size: 12px; opacity: 0.75; }
.snd-url-input {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  padding: 4px 7px;
  background: #0f172a;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 7px;
  color: white;
}
.snd-url-input:focus { outline: none; border-color: var(--accent); }

@media (max-width: 640px) {
  .settings-panel-body { grid-template-columns: 1fr; height: auto; }
}

/* Confetti burst particles fired from the edges of the winner box.
   --tx/--ty/--tr drive the final translate+rotate; gravity is baked into
   --ty by the JS so all particles eventually fall. z-index sits below the
   winner-overlay (z-index: 30) so particles spawn *behind* the popup and
   visually emerge as they animate out past its edges. */
.burst-confetti {
  position: fixed;
  pointer-events: none;
  border-radius: 2px;
  z-index: 25;
  animation: confettiBurst linear forwards;
}
@keyframes confettiBurst {
  to { transform: translate(var(--tx), var(--ty)) rotate(var(--tr)); opacity: 0; }
}

.shard {
  position: fixed;
  pointer-events: none;
  border-radius: 3px;
  animation: shardFly linear forwards;
}
/* Image debris launched alongside the confetti shards in launchExplosion.
   Reuses shardFly so its --tx/--ty/--tr CSS vars drive the same fly-out
   transform, fading out over the chosen animation-duration. */
.explode-img {
  position: fixed;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
  z-index: 1000;
  animation: shardFly linear forwards;
  filter: drop-shadow(0 0 8px rgba(0,0,0,0.5));
}
@keyframes shardFly {
  to { transform: translate(var(--tx), var(--ty)) rotate(var(--tr)); opacity: 0; }
}

@keyframes shake {
  0%,100% { transform: translate(0,0)      rotate(0deg);    }
  10%      { transform: translate(-8px, 4px) rotate(-1.5deg); }
  20%      { transform: translate( 9px,-5px) rotate( 1.2deg); }
  30%      { transform: translate(-6px, 7px) rotate(-0.8deg); }
  40%      { transform: translate( 8px,-3px) rotate( 1.5deg); }
  50%      { transform: translate(-9px, 5px) rotate(-1deg);   }
  60%      { transform: translate( 6px, 6px) rotate( 0.9deg); }
  70%      { transform: translate(-7px,-4px) rotate(-1.3deg); }
  80%      { transform: translate( 5px, 3px) rotate( 0.7deg); }
  90%      { transform: translate(-4px,-6px) rotate(-0.5deg); }
}
.shaking { animation: shake 0.55s ease forwards; }

/* ── Confirmation modal ── */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 210;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
.modal-overlay.show { opacity: 1; pointer-events: auto; }
.modal-box {
  background: #0f172a;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 20px;
  padding: 28px 32px;
  max-width: 380px;
  width: 90%;
  box-shadow: 0 20px 60px rgba(0,0,0,0.6);
  text-align: center;
}
.modal-msg { font-size: 16px; margin-bottom: 22px; line-height: 1.5; opacity: 0.9; }
.modal-actions { display: flex; gap: 12px; justify-content: center; }
.modal-actions button { min-width: 110px; }
.modal-actions button:not(.danger) { background: #475569; color: white; }

/* ── Raw data modal ── */
.raw-data-box {
  background: #0f172a;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 20px;
  padding: 24px;
  width: min(92vw, 920px);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.6);
}
.raw-data-title { font-size: 18px; font-weight: bold; margin: 0; display: flex; align-items: center; gap: 6px; }
.raw-data-hint  { font-size: 12px; opacity: 0.6; margin: 0; line-height: 1.4; }
.raw-data-error { font-size: 13px; color: var(--danger); min-height: 18px; }
#rawDataText {
  flex: 1;
  min-height: 320px;
  width: 100%;
  background: #020617;
  color: #f8fafc;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 10px;
  padding: 12px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.5;
  resize: vertical;
}
.raw-data-actions { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; }
.raw-data-actions button { background: #475569; color: white; min-width: 130px; padding: 10px 14px; }
.raw-data-actions button.primary { background: var(--accent); color: black; }

/* ── Image Gallery modal ── */
.gallery-box {
  background: #0f172a;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 20px;
  padding: 24px;
  width: min(92vw, 720px);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.6);
}
.gallery-title { font-size: 18px; font-weight: bold; margin: 0; display: flex; align-items: center; gap: 6px; }
.gallery-hint  { font-size: 12px; opacity: 0.6; margin: 0; line-height: 1.4; }
.gallery-hint code { background: rgba(255,255,255,0.08); padding: 1px 5px; border-radius: 4px; }
.gallery-grid {
  flex: 1;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: 10px;
  padding: 4px;
  min-height: 200px;
  scrollbar-gutter: stable;
}
.gallery-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px;
  border-radius: 12px;
  border: 2px solid transparent;
  background: rgba(255,255,255,0.04);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.gallery-item:hover { background: rgba(255,255,255,0.09); }
.gallery-item.selected {
  border-color: var(--accent);
  background: rgba(34,197,94,0.12);
}
.gallery-item img {
  width: 100%;
  height: 84px;
  object-fit: contain;
  pointer-events: none;
}
.gallery-caption {
  font-size: 10px;
  opacity: 0.6;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.gallery-empty { grid-column: 1 / -1; text-align: center; opacity: 0.5; padding: 40px 0; }
.gallery-actions { display: flex; gap: 10px; justify-content: flex-end; }
.gallery-actions button { background: #475569; color: white; min-width: 120px; padding: 10px 14px; }
.gallery-actions button.primary { background: var(--accent); color: black; }

/* ── Mobile single-column layout ── */
@media (max-width: 640px) {
  body {
    overflow: auto;
    padding: 12px;
  }

  .app {
    height: auto;
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
    row-gap: 16px;
    column-gap: 0;
  }

  .panel-toggle { display: none; }

  /* Wheel on top */
  .wheel-wrap {
    order: -1;
    height: 45vh;
    min-height: 260px;
  }

  /* Sidebar fills full width and sizes to content */
  .sidebar-wrapper {
    width: 100% !important;
    height: auto;
    min-height: auto;
  }

  .sidebar-wrapper > .panel {
    flex: none;
    width: 100%; /* override fixed 520px — panel is fluid on mobile */
    min-height: auto;
    height: auto;
  }

  /* Unwind flex-scroll chain so sidebar grows naturally */
  .entrant-section {
    flex: none;
    min-height: auto;
    height: auto;
  }

  .entrant-scroll-wrap {
    flex: none;
    min-height: auto;
    overflow: visible;
    height: auto;
  }

  .entrant-list {
    height: auto;
    overflow: visible;
  }

  /* Wheels list — drop the fixed-height scroll cap on mobile so the list
     grows naturally with its content, matching the entrant-list behavior
     above. The scroll fades self-hide because scrollHeight === clientHeight. */
  .wheel-scroll-wrap {
    overflow: visible;
  }
  .wheel-list-scroll {
    height: auto;
    overflow: visible;
  }
}
