:root {
  --bg: #0b0709;
  --panel: #1a1215;
  --line: #3a2a30;
  --text: #f1e8e4;
  --dim: #9a8a8f;
  --ok: #4caf7a;
  --bad: #ff4040;
  --warn: #e0a030;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.3 -apple-system, system-ui, "Segoe UI", sans-serif;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
  overscroll-behavior: none;
}

body {
  display: flex;
  flex-direction: column;
  padding: env(safe-area-inset-top) 12px env(safe-area-inset-bottom);
}

header {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 0;
}

.conn { color: var(--ok); font-weight: 600; white-space: nowrap; }
.conn::before { content: "● "; }
.offline .conn { color: var(--bad); }

.volume { flex: 1; display: flex; align-items: center; gap: 10px; color: var(--dim); }
.volume input { flex: 1; height: 36px; accent-color: #c0392b; border-radius: 8px; }
.volume input:disabled { opacity: 0.35; }

/* Collapsed: one banner, wrapped, at most three lines, with the count of the
   rest beside it where it cannot be cut off. Tap to see everything in full. */
#banners { max-height: 45vh; overflow-y: auto; }
#banners.expandable { cursor: pointer; }
#banners:not(.expanded) .banner .text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

.banner {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  margin: 0 0 8px;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 14px;
  -webkit-user-select: text;
  user-select: text;
}
.banner .text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.banner .more { flex: none; white-space: nowrap; font-weight: 700; }
.banner.bad { background: #5a1010; border: 1px solid var(--bad); }
.banner.warn { background: #4a3508; border: 1px solid var(--warn); }

main { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 16px; }

/* The world picker: one tab per group of themes. It sits outside the
   scrolling area so the mark on the playing world is always in view. */
.worlds { display: flex; gap: 6px; margin: 0 0 10px; }
.worlds[hidden], .heading[hidden] { display: none; }
.tab {
  flex: 1 1 0;
  min-width: 0;
  min-height: 48px;
  padding: 6px 4px;
  border-width: 1px;
  border-radius: 10px;
  color: var(--dim);
  font-size: 13px;
  line-height: 1.15;
  touch-action: manipulation;
}
.tab .label { overflow-wrap: anywhere; }
.tab.selected { background: var(--line); border-color: var(--text); color: var(--text); }
/* The world that is playing, whichever tab is open. */
.tab.playing { border-bottom: 4px solid var(--warn); }
.tab.playing .label::before { content: "● "; color: var(--warn); }

.heading {
  margin: 0 0 -8px;
  color: var(--dim);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.grid { display: grid; gap: 10px; }
.grid.themes { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
.grid.effects { grid-template-columns: repeat(3, 1fr); }

button {
  position: relative;
  overflow: hidden;
  min-height: 76px;
  padding: 8px;
  border: 2px solid var(--color, var(--line));
  border-radius: 12px;
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-weight: 700;
  -webkit-tap-highlight-color: transparent;
}
button .fill {
  position: absolute;
  inset: 0;
  background: var(--color, var(--line));
  transform: scaleX(0);
  transform-origin: left;
}
button .label { position: relative; }

.theme { min-height: 88px; font-size: 18px; text-transform: uppercase; letter-spacing: 0.04em; }
.theme.current .fill { transform: scaleX(1); }
.theme.current.fading .fill { animation: fill var(--fade, 4s) linear both; }
@keyframes fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.effect:active { filter: brightness(1.4); }
.effect.flash .fill { animation: flash 0.5s ease-out; }
@keyframes flash { from { transform: scaleX(1); opacity: 1; } to { transform: scaleX(1); opacity: 0; } }

button:disabled, .offline main button, .offline .blackout { opacity: 0.35; }

/* Someone else's change has just moved the buttons: for a moment they are
   dimmed and ignore touches (LAYOUT_LOCKOUT_MS in app.js). */
main.settling button:not(:disabled), .worlds.settling button { opacity: 0.5; }

/* The server refused the press (or the touch was ignored): a brief red outline. */
.failed { animation: failed 0.6s ease-out; }
@keyframes failed {
  from { outline: 3px solid var(--bad); outline-offset: 2px; }
  to { outline: 3px solid transparent; outline-offset: 2px; }
}

footer { padding: 12px 0; }
/* touch-action: none so a drifting thumb cannot turn the hold into a pan. */
.blackout { width: 100%; --color: #555; font-size: 18px; text-transform: uppercase; touch-action: none; }
.blackout.holding .fill { transition: transform 0.5s linear; transform: scaleX(1); }
.blackedout .blackout { --color: var(--bad); }
.blackedout .blackout .fill { transform: scaleX(1); }
/* Holding to resume drains the fill. */
.blackedout .blackout.holding .fill { transition: transform 0.5s linear; transform: scaleX(0); }
/* Just after a blackout or resume the button ignores touches for a moment. */
.blackout.locked { opacity: 0.6; }
