/* View-specific styles: login, admin board, result sheet. */

/* ---------- Shared form bits ---------- */

.field {
  margin-bottom: var(--space-3);
}

.field__label {
  display: block;
  margin-bottom: var(--space-1);
  font-size: 0.75rem;
  font-weight: var(--weight-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-mute);
}

.field__input {
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-3);
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.field__input--area {
  min-height: 8rem;
  resize: vertical;
  font-family: var(--font-num);
  line-height: 1.5;
}

.row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.row--center {
  justify-content: center;
  margin-top: var(--space-3);
}

.field__input--inline {
  flex: 1;
  width: auto;
  min-width: 10rem;
}

.namelist {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

.namelist__row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

.namelist__name {
  flex: 1;
}

.namelist__remove {
  min-height: 1.75rem;
  padding: 0 var(--space-3);
  font-size: 0.78rem;
}

/* Element+class beats the plain .btn--danger class it's layered on
 * (confirmBtn.js always applies it) — red at rest reads as an alarm on
 * every row in the list; muted until the two-step confirm arms it. */
button.namelist__remove {
  border-width: 1px;
  border-color: var(--border);
  color: var(--text-mute);
  background: transparent;
}

.hint {
  margin: 0 0 var(--space-3);
  color: var(--text-mute);
  font-size: 0.9rem;
}

/* Step-by-step help, collapsible so it stays out of the way once learned. */
.guide {
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  font-size: 0.9rem;
}

.guide > summary {
  font-weight: var(--weight-bold);
  cursor: pointer;
}

.guide ol {
  margin: var(--space-2) 0;
  padding-left: 1.4rem;
}

.guide li {
  margin-bottom: var(--space-2);
}

.guide p {
  margin: var(--space-2) 0 0;
  color: var(--text-mute);
}

/* A refused password change is a normal outcome, not a system fault — it gets
 * the warning colour, not the red banner treatment reserved for errors. */
.hint--error {
  color: var(--ui-warn);
  font-weight: var(--weight-medium);
}

.seg {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.seg__btn {
  min-width: var(--tap-min);
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-weight: var(--weight-bold);
}

.seg__btn[aria-pressed="true"] {
  border-color: var(--ui-accent);
  background: var(--ui-accent);
  color: #ffffff;
}

.btn--danger {
  border-color: var(--gruppe-rot);
  color: var(--gruppe-rot);
}

.btn--danger.is-armed {
  background: var(--gruppe-rot);
  border-color: var(--gruppe-rot);
  color: #ffffff;
}

.banner--error {
  border-color: var(--gruppe-rot);
}

/* ---------- Login ---------- */

.login {
  max-width: 24rem;
  margin: var(--space-6) auto;
}

.login__stage {
  display: flex;
  justify-content: center;
  margin-bottom: -2.5rem;
  position: relative;
  z-index: 1;
}

.loginball {
  width: 5rem;
  height: 5rem;
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.3));
  animation: ballBounce 1.6s ease-in-out infinite;
}

@keyframes ballBounce {
  0%,
  100% {
    transform: translateY(0) scale(1, 1);
  }
  45% {
    transform: translateY(-14px) scale(0.97, 1.03);
  }
  55% {
    transform: translateY(-14px) scale(0.97, 1.03);
  }
  70% {
    transform: translateY(0) scale(1.04, 0.96);
  }
}

.login__card {
  padding: var(--space-6) var(--space-5) var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.login__title {
  margin-bottom: var(--space-1);
  text-align: center;
}

.login__sub {
  margin-bottom: var(--space-4);
  text-align: center;
  color: var(--text-mute);
  font-size: 0.9rem;
}

.login__remember {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-size: 0.9rem;
  color: var(--text-mute);
}

.login__remember input {
  width: 1.1rem;
  height: 1.1rem;
}

.login__submit {
  width: 100%;
}

.login__error:not(:empty) {
  margin-bottom: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 2px solid var(--gruppe-rot);
  border-radius: var(--radius-sm);
  color: var(--gruppe-rot);
  font-weight: var(--weight-medium);
}

.login__hint {
  margin: var(--space-4) 0 0;
  text-align: center;
  color: var(--text-mute);
  font-size: 0.8rem;
}

/* ---------- Assignment board ---------- */

.board {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3);
}

.board__groups {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  /* `pan-y` alone was wrong (GSF-0072): it tells the browser only vertical
   * panning is this element's own native gesture, which — since this row's
   * overflow is horizontal — disabled the one gesture it actually needs. A
   * quick swipe (below the long-press threshold in dragboard.js) falls
   * through to the browser with no `preventDefault()`, meaning it was the
   * browser's own touch-action restriction, not JS, silently eating it.
   * `pan-x pan-y` allows both: horizontal scrolls this row, vertical still
   * reaches the page (this element has no vertical overflow of its own). */
  touch-action: pan-x pan-y;
  -webkit-overflow-scrolling: touch;
  /* Correct general practice — this element owns its horizontal scroll and
   * should not hand the gesture off elsewhere — but it did NOT fix the
   * actual Chrome-on-iPad bug it was added for (GSF-0075). Root cause turned
   * out to be Chrome's own swipe-to-switch-tabs gesture, which only appears
   * once more than one tab is open (confirmed on the device: one tab
   * scrolls fine, several tabs break it). That gesture lives in Chrome's
   * native app shell, entirely outside the page, so no page-level CSS —
   * this property included — can touch it. The actual fix is the tap-to-
   * scroll `.board__nav` buttons below (GSF-0076): a tap is never a swipe
   * candidate, so no gesture recognizer can claim it out from under us. */
  overscroll-behavior-x: contain;
  /* macOS hides the scrollbar until something is actively scrolling, and a
   * plain scroll-wheel mouse (no trackpad, no horizontal wheel) has no way
   * to trigger that — the lane past the fold then looks like it does not
   * exist. A thin, always-visible bar plus the wheel handler in admin.js
   * give a desktop mouse a way in. */
  scrollbar-width: thin;
  padding-bottom: var(--space-2);
}

.board__groups::-webkit-scrollbar {
  height: 0.5rem;
}

.board__groups::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--radius-sm);
}

/* Tap-to-scroll fallback for the lane row (GSF-0076) — see admin.js for why
 * a tap, not a swipe, is the only gesture no browser's own navigation
 * feature can steal. */
.board__groups-wrap {
  position: relative;
  min-width: 0;
}

.board__nav {
  position: absolute;
  top: 50%;
  z-index: 5;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: 1.1rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transform: translateY(-50%);
}

.board__nav:disabled {
  opacity: 0;
  pointer-events: none;
}

.board__nav--left {
  left: 0.25rem;
}

.board__nav--right {
  right: 0.25rem;
}

.lane {
  flex: 0 0 11rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-2);
  border: 2px solid var(--border);
  border-left: 6px solid var(--edge, var(--border-strong));
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  min-height: 12rem;
}

.lane--pool {
  flex: 0 0 auto;
  min-height: 4rem;
  border-left-color: var(--border-strong);
  background: var(--surface-sunken);
}

.lane--pool .lane__chips {
  flex-direction: row;
  flex-wrap: wrap;
}

.lane--pool .chip--player {
  width: auto;
  flex: 0 0 auto;
}

.lane.is-target {
  border-color: var(--ui-accent);
  background: var(--ui-accent-soft);
}

.lane__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.lane__title {
  font-weight: var(--weight-bold);
  font-size: 0.95rem;
}

.lane__count {
  margin-left: auto;
  color: var(--text-mute);
  font-size: 0.85rem;
}

.lane__chips {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1;
}

.lane__drop {
  min-height: var(--tap-min);
  border: 2px dashed var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-mute);
  font-size: 0.85rem;
}

/* One tap sets the colour. Four ~36 px swatches, no dropdown, no modal. */
.colorpills {
  display: flex;
  gap: 2px;
}

.colorpills__pill {
  flex: 1;
  height: 1.6rem;
  border: 2px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--dot);
}

.colorpills__pill[aria-pressed="true"] {
  border-color: var(--text);
  box-shadow: inset 0 0 0 2px var(--surface);
}

.chip--player {
  display: block;
  width: 100%;
  padding: var(--space-2);
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 0.9rem;
  font-weight: var(--weight-medium);
  text-align: left;
  /* Without these iOS opens the copy/look-up callout on long press and eats
   * the gesture — the single most common failure of touch drag-and-drop. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

.chip--player[aria-selected="true"] {
  border-color: var(--ui-accent);
  box-shadow: 0 0 0 3px var(--ui-accent-soft);
}

/* touch-action is set only while dragging; a permanent `none` would kill
 * scrolling everywhere. */
.chip--player.is-dragging {
  opacity: 0.35;
  touch-action: none;
}

.chip--ghost {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 900;
  pointer-events: none;
  will-change: transform;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
  opacity: 0.95;
}

.selectbar {
  display: none;
  padding: var(--space-2) var(--space-4);
  border-top: 2px solid var(--ui-accent);
  background: var(--ui-accent-soft);
  font-weight: var(--weight-medium);
}

.selectbar.is-active {
  display: block;
}

/* ---------- Result sheet ---------- */

body.has-sheet {
  overflow: hidden;
}

.sheet__backdrop {
  position: fixed;
  inset: 0;
  z-index: 800;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: var(--space-3);
  background: rgba(8, 12, 20, 0.55);
}

@media (min-width: 40rem) {
  .sheet__backdrop {
    align-items: center;
  }
}

.sheet {
  width: min(30rem, 100%);
  max-height: 92vh;
  overflow-y: auto;
  padding: var(--space-4);
  padding-bottom: max(var(--space-4), env(safe-area-inset-bottom));
  border: 2px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
}

.sheet__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.sheet__round {
  font-weight: var(--weight-bold);
}

.sheet__hint {
  margin-left: auto;
  color: var(--text-mute);
  font-size: 0.85rem;
}

.sheet__actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.sheet__action {
  flex: 1;
}

.sheet__error:not(:empty) {
  margin-top: var(--space-3);
  color: var(--gruppe-rot);
  font-weight: var(--weight-medium);
}

.conflict {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 2px solid var(--ui-warn);
  border-radius: var(--radius-sm);
}

.conflict__text {
  margin-bottom: var(--space-2);
  font-weight: var(--weight-medium);
}

/* ---------- Score pad ---------- */

.scorepad__fields {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.scorepad__colon {
  align-self: center;
  font-size: 1.6rem;
  font-weight: var(--weight-bold);
  color: var(--text-mute);
}

.scorefield {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-2);
  border: 3px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  text-align: center;
}

.scorefield.is-active {
  border-color: var(--ui-accent);
  background: var(--ui-accent-soft);
}

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

/* A div, not an input: a focused input makes iOS raise the system keyboard on
 * top of this pad and halve the usable area. */
.scorefield__value {
  font-size: 2.4rem;
  font-weight: var(--weight-bold);
  line-height: 1.1;
}

.numpad {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-2);
}

.numpad__key {
  min-height: 3.4rem;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 1.4rem;
  font-weight: var(--weight-bold);
}

.numpad__key:active {
  background: var(--ui-accent-soft);
}

.numpad__key--wide {
  grid-column: span 5;
}

/* ---------- Connection status ---------- */

.status-strip {
  max-width: var(--content-max);
}

.status-strip:not(:empty) {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.banner--offline {
  border-color: var(--gruppe-orange);
}

.banner--conflict {
  display: block;
  border-color: var(--ui-warn);
}

/* A queued write is dashed and never reads as a stored score. */
.match__score--waiting {
  border-style: dashed;
  border-color: var(--gruppe-orange);
  color: var(--gruppe-orange);
  font-size: 0.8rem;
  font-weight: var(--weight-bold);
}

/* ---------- Clickable fixtures ---------- */

.match--editable {
  cursor: pointer;
}

.match--editable:hover {
  background: var(--surface-2);
}

/* ---------- Round timer ---------- */

.timer-host:not(:empty) {
  max-width: var(--content-max);
  margin-bottom: var(--space-4);
}

.timer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 2px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.timer__clock {
  font-size: 2.6rem;
  font-weight: var(--weight-bold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.timer__clock.is-urgent {
  color: var(--gruppe-rot);
}

.timer__meta {
  color: var(--text-mute);
  font-size: 0.85rem;
}

/* Temporary clock diagnostics (GSF-0058) — deliberately unobtrusive, and
 * removed together with `offsetLine()` once the jumping is confirmed gone. */
.timer__meta--diag {
  font-family: var(--font-num);
  font-size: 0.72rem;
  opacity: 0.7;
}

.timer__controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-left: auto;
}

/* Full-screen colour signal. The timer must still work when the ringer switch
 * is off or iOS has suspended the audio context. */
.flash {
  position: fixed;
  inset: 0;
  z-index: 700;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.18s ease-out;
}

.flash--warn {
  background: var(--gruppe-orange);
  opacity: 0.55;
}

.flash--end {
  background: var(--gruppe-rot);
  opacity: 0.7;
}

/* ---------- Documentation ---------- */

.changelog__row {
  display: grid;
  grid-template-columns: 9rem 1fr;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.changelog__row:last-child {
  border-bottom: 0;
}

.changelog__meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  align-items: flex-start;
}

.changelog__date {
  color: var(--text-mute);
  font-size: 0.78rem;
}

.changelog__text {
  min-width: 0;
}

.changelog__id {
  font-family: var(--font-num);
  font-size: 0.78rem;
  color: var(--text-mute);
}

/* One release per <details>: the newest is open, older ones fold away, so
 * sixty entries stay scannable without any state kept in JavaScript. */
.release__summary {
  cursor: pointer;
  list-style: none;
}

.release__summary::-webkit-details-marker {
  display: none;
}

/* The marker has to say "foldable" without relying on the native triangle,
 * which is unstyleable across browsers and invisible on iOS Safari. */
.release__summary::before {
  content: "▸";
  color: var(--text-mute);
  transition: transform 120ms ease;
}

.release[open] > .release__summary::before {
  transform: rotate(90deg);
}

.release__summary:focus-visible {
  outline: 3px solid var(--ui-accent);
  outline-offset: -3px;
}

@media (prefers-reduced-motion: reduce) {
  .release__summary::before {
    transition: none;
  }
}

@media (max-width: 34rem) {
  .changelog__row {
    grid-template-columns: 1fr;
  }
}

/* ---------- Tournament management ---------- */

.tourrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
}

.tourrow:first-child {
  padding-top: 0;
}

.tourrow__main {
  flex: 1;
  min-width: 12rem;
}

.tourrow__name {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-bold);
}

.tourrow__meta {
  color: var(--text-mute);
  font-size: 0.85rem;
}

.tourrow .row {
  margin-left: auto;
}

/* ---------- Background picker ---------- */

.bgpicker {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.bgpicker__btn {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 9rem;
  padding: var(--space-1);
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: inherit;
}

.bgpicker__btn[aria-pressed="true"] {
  border-color: var(--ui-accent);
  box-shadow: 0 0 0 2px var(--ui-accent-soft);
}

.bgpicker__btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

.bgpicker__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  object-fit: cover;
}

/* The "no image" tile needs to read as deliberately empty, not as a broken
 * thumbnail — a dashed border and a dash do that without another asset. */
.bgpicker__thumb--none {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--border);
}

.bgpicker__thumb--none::after {
  content: "–";
  color: var(--text-mute);
  font-size: 1.4rem;
  font-weight: var(--weight-bold);
}

.bgpicker__label {
  text-align: center;
  font-size: 0.85rem;
  font-weight: var(--weight-medium);
  color: var(--text);
}

/* ---------- Screen lock ---------- */

/* Above the result sheet (800) and the flash layer: while locked, nothing
 * underneath may be reachable — that is the whole point of the overlay. */
.lock {
  position: fixed;
  inset: 0;
  z-index: 950;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  background: rgba(8, 12, 20, 0.92);
}

.lock__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  width: min(20rem, 100%);
  padding: var(--space-4);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

.lock__title {
  font-size: 1.15rem;
  font-weight: var(--weight-bold);
}

.lock__clock {
  font-size: 2rem;
  font-weight: var(--weight-bold);
  color: var(--text-mute);
}

.lock__dots {
  display: flex;
  gap: var(--space-3);
}

.lock__dot {
  width: 0.9rem;
  height: 0.9rem;
  border: 2px solid var(--border-strong);
  border-radius: 50%;
}

.lock__dot.is-filled {
  background: var(--ui-accent);
  border-color: var(--ui-accent);
}

.lock__dots--wrong {
  animation: lock-shake 400ms;
}

.lock__dots--wrong .lock__dot {
  border-color: var(--gruppe-rot);
}

@keyframes lock-shake {
  25% { transform: translateX(-6px); }
  75% { transform: translateX(6px); }
}

@media (prefers-reduced-motion: reduce) {
  .lock__dots--wrong {
    animation: none;
  }
}

.lock__pad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  width: 100%;
}

.lock__key {
  min-height: 3.25rem;
  border: 2px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text);
  font-size: 1.35rem;
  font-weight: var(--weight-bold);
}

.lock__key--muted {
  color: var(--text-mute);
  font-size: 1.05rem;
}

.lock__hint {
  color: var(--text-mute);
  font-size: 0.85rem;
  text-align: center;
}

/* Belt and braces: the overlay covers the app, and the app cannot scroll
 * underneath it either. */
body.is-locked {
  overflow: hidden;
}

/* ---------- "Turnier" board (GSN-0079) ---------- */

.turnier {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  /* Fill the height #view hands down (GSN-0084) so the group lanes grow into
   * the space that used to sit empty under the courts. */
  flex: 1;
  min-width: 0;
  min-height: 0;
}

.turnier__error {
  margin: 0;
}

.turnier__error:empty {
  display: none;
}

/* Top half: one column per group, growing to fill the height. Bottom half: six
 * courts at their natural (short) height, flush underneath. Both rows fill the
 * full width — the N groups spread evenly across it, the six courts likewise —
 * so the halves span the same width and, at six groups, line up one to one.
 * `--turnier-cols` (the group count) is set on `.turnier__top` from turnier.js;
 * N is 1..7 (MAX_GROUPS), so nothing has to scroll sideways. */
.turnier__split {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
  min-height: 0;
}

.turnier__top {
  display: grid;
  grid-template-columns: repeat(var(--turnier-cols, 6), minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  gap: var(--space-2);
  flex: 1 1 auto;
  min-height: 0;
  padding-bottom: var(--space-2);
}

.turnier__courts {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-2);
  flex: 0 0 auto;
}

.turnier__group {
  min-width: 0;
  min-height: 0;
  /* Double the colour edge (was 6px on .lane) and lift the head into a filled
   * group-coloured bar (GSN-0088, retuned GSF-0089). Header fill/ink come from
   * --headbg / --headfg (components.css) — fixed, recognisable hues. */
  border-left-width: 12px;
  border-left-color: var(--headbg, var(--edge));
  padding-top: 0;
}

.turnier__group .lane__head {
  margin: 0 calc(-1 * var(--space-2));
  padding: var(--space-2);
  background: var(--headbg);
  color: var(--headfg);
  border-radius: 0 var(--radius-sm) 0 0;
}

.turnier__group .lane__count {
  color: var(--headfg);
  opacity: 0.8;
}

.turnier__group .lane__chips,
.lane--court .lane__chips {
  overflow-y: auto;
}

.lane--court {
  min-width: 0;
  min-height: 8rem;
}

/* Pairing chip — a two-name card. Draggable ones also carry .chip--player for
 * the shared drag mechanics (ghost, callout suppression, .is-dragging). */
.chip--pair {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 0.85rem;
}

.chip--pair__names {
  font-weight: var(--weight-medium);
}

.chip--pair__note {
  color: var(--text-mute);
  font-size: 0.78rem;
}

.chip--pair.is-playing {
  padding: var(--space-2);
  border: 2px dashed var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  color: var(--text-mute);
}

.chip--pair--done {
  padding: var(--space-2);
  border: 2px solid var(--edge, var(--border));
  border-radius: var(--radius-sm);
  background: var(--surface);
}

.chip--pair__side.is-winner {
  font-weight: var(--weight-bold);
}

.chip--pair__score {
  color: var(--text-mute);
}

/* On a court: shown in the group's colour. */
.chip--court {
  padding: var(--space-2);
  border: 2px solid var(--edge, var(--border-strong));
  border-left-width: 6px;
  border-radius: var(--radius-sm);
  background: var(--fill, var(--surface));
}

.chip--court__actions {
  display: flex;
  gap: var(--space-1);
}

.btn--small {
  min-height: 2rem;
  padding: 2px var(--space-2);
  font-size: 0.8rem;
}

/* ---------- Slide-out menu (first off-canvas pattern; cf. .sheet) ---------- */

.turnier__handle {
  position: fixed;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 39;
  width: 1.5rem;
  height: 4rem;
  border: 2px solid var(--border-strong);
  border-left: 0;
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--sidebar-bg);
  color: #ffffff;
  font-size: 0.9rem;
}

.turnier__scrim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: rgba(0, 0, 0, 0.35);
}

.turnier__drawer {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: 41;
  width: 12rem;
  padding: var(--space-3) var(--space-2);
  padding-top: max(var(--space-3), env(safe-area-inset-top));
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--sidebar-bg);
  color: var(--sidebar-text);
  overflow-y: auto;
  transform: translateX(-100%);
  transition: transform 0.2s ease;
}

.turnier__drawer.is-open {
  transform: translateX(0);
}

.turnier__drawer-nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.turnier__drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.turnier__pin {
  padding: 2px var(--space-2);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.08);
  color: var(--sidebar-text);
  font-size: 0.75rem;
  white-space: nowrap;
}

.turnier__pin[aria-pressed="true"] {
  background: rgba(255, 255, 255, 0.22);
  border-color: currentColor;
}

@media (max-width: 34rem) {
  /* On a real phone the bottom bar is the navigation; the drawer would just
   * duplicate it. */
  .turnier__handle,
  .turnier__scrim,
  .turnier__drawer {
    display: none;
  }
}
