/* --- Temas claro/oscuro (Bloque 14.5, segunda pieza) ---
   Manuel pidió dos temas con colores pastel: uno claro con los colores de
   los New York Knicks (azul/naranja) y uno oscuro "bonito" sin más
   indicación, a nuestro criterio. Se aplican con el atributo
   `data-theme="claro"|"oscuro"` en <html> (ver `applyTheme` en app.js, que
   también lo persiste en preferencias.json junto a los colores del editor
   de cancha). El tema oscuro es el que ya había desde el principio del
   proyecto, ahora movido a su propio bloque para poder alternar con el
   claro sin duplicar el resto de la hoja de estilos: casi todo el resto del
   CSS usa estas variables (--bg/--panel/--sidebar/--text/--muted/--accent/
   --border), así que cambia de golpe con el tema. */
:root,
:root[data-theme='oscuro'] {
  color-scheme: dark;
  --bg: #161a22;
  --panel: #1e2430;
  --panel-alt: #232a37;
  --sidebar: #10141b;
  --text: #f0f0f0;
  --muted: #9aa4b2;
  --accent: #e67e22;
  --accent-contrast: #14181f;
  --border: #2a323f;
  --danger: #e08a8a;
  --danger-strong: #ffb3b3;
  --success: #8fd19e;

  /* Colores personalizables del editor de cancha (Bloque 14.5): valores por
     defecto, iguales a los que ya había fijos antes de esta pieza. Se
     sobrescriben en tiempo de ejecución (app.js, `applyColorPrefs`) con lo
     que el usuario elija en el panel "🎨 Colores", y esos mismos valores se
     mandan también a la exportación a PDF (ver PRINT_CSS en main.js) para
     que pantalla y PDF coincidan siempre. Son independientes del tema visual
     de la app (se guardan y aplican igual en claro y en oscuro). */
  /* Negro por defecto para todos (Bloque 14.5, novena pieza): Manuel pidió
     "color por defecto de todos los elementos gráficos siempre negro" —
     antes cada tipo tenía un color distinto de fábrica (azul/rojo/naranja
     para atacante/defensor/cono, casi blanco para las líneas). Sigue siendo
     100% personalizable desde el panel "🎨 Colores"; esto solo cambia el
     valor con el que arranca una instalación nueva (o tras pulsar
     "Restablecer"). */
  --color-atacante: #000000;
  --color-defensor: #000000;
  --color-cono: #000000;
  --color-linea-movimiento: #000000;
  --color-linea-pase: #000000;
  --color-linea-tiro: #000000;
  --color-linea-bote: #000000;
  --color-linea-bloqueo: #000000;
  --color-linea-mano-amano: #000000;
  /* Zonas (Bloque 14.5, décima pieza): un único color por defecto para las
     tres formas (rectángulo/círculo/triángulo) — se dibujan con relleno
     traslúcido (ver .zone-visible más abajo), así que aunque el color de
     partida sea negro como el resto de elementos, la zona se lee como un
     área semitransparente y no como una figura sólida. */
  --color-zona: #000000;
  /* Círculo del balón (29/08/2026, revisión técnica interna): variable
     nueva y aparte de --color-atacante -- antes el círculo del balón
     reutilizaba siempre el mismo color que el número del atacante, así que
     un atacante con balón y color propio se quedaba con el número
     invisible sobre su propio círculo (mismo color exacto). Mismo negro por
     defecto que el resto ("todos los colores por defecto siempre negro"),
     100% editable aparte desde el panel "🎨 Colores" o por jugador. */
  --color-balon: #000000;
  /* Cono plano (29/08/2026): nuevo elemento colocable ("un cono plano, que
     se representa con un donut"), con su propio color por defecto, aparte
     de --color-cono del cono normal -- mismo criterio de negro por defecto
     que el resto de elementos gráficos. */
  --color-cono-plano: #000000;
  /* Entrenador (15/09/2026, a petición de Manuel: "el entrenador con icono
     como silbato"): color propio, mismo criterio de negro por defecto que
     el resto. */
  --color-entrenador: #000000;
  /* Balón como elemento colocable (15/09/2026): en un primer intento este
     icono reutilizaba --color-balon (el mismo círculo de posesión del
     atacante), pero Manuel pidió después "métele administración de
     colores" -- es decir, su propia entrada independiente en el panel
     "🎨 Colores", igual que cono/cono plano, no un color compartido con otro
     elemento. Mismo criterio de negro por defecto que el resto. */
  --color-balon-elemento: #000000;
}

/* Tema claro (Bloque 14.5): pastel, inspirado en los colores clásicos de
   los New York Knicks (azul "Knicks Blue" y naranja "Knicks Orange"),
   suavizados para que el conjunto se vea tranquilo en pantallas grandes de
   trabajo y no como una app de fan deportivo. */
:root[data-theme='claro'] {
  color-scheme: light;
  --bg: #f3f6fb;
  --panel: #ffffff;
  --panel-alt: #eef2fa;
  --sidebar: #e8eefa;
  --text: #1c2634;
  --muted: #5c6b80;
  --accent: #f0883e;
  --accent-contrast: #241608;
  --border: #d7deea;
  --danger: #b23a3a;
  --danger-strong: #8f2020;
  --success: #2f8f52;

  /* La pista del editor mantiene siempre su mismo aspecto (naranja/marrón,
     fija en court.js/PRINT_CSS) sea cual sea el tema de la app alrededor —
     por eso estos valores por defecto se quedan igual que en el tema
     oscuro, casi blancos, para verse bien sobre esa pista. Los reemplaza en
     cuanto arranca la app el valor guardado en preferencias.json (o este
     mismo valor por defecto), sea cual sea el tema visual elegido. */
  --color-atacante: #000000;
  --color-defensor: #000000;
  --color-cono: #000000;
  --color-linea-movimiento: #000000;
  --color-linea-pase: #000000;
  --color-linea-tiro: #000000;
  --color-linea-bote: #000000;
  --color-linea-bloqueo: #000000;
  --color-linea-mano-amano: #000000;
  --color-zona: #000000;
  --color-balon: #000000;
  --color-cono-plano: #000000;
  --color-entrenador: #000000;
  --color-balon-elemento: #000000;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, "Noto Sans", sans-serif;
  background: var(--bg);
  color: var(--text);
}

/* `height: 100vh` (Bloque 14.5, sexta pieza), no `min-height`: antes, en
   cualquier vista con contenido alto (Jugadas con varios gráficos, un
   equipo largo...) toda la página crecía más allá de la ventana y el
   scroll de la propia página se llevaba por delante también la barra
   lateral, así que había que desplazarse hacia abajo para volver a ver las
   pestañas "Jugadas/Ejercicios/Biblioteca/Equipos" — justo lo contrario de
   "usar los scroll de pantalla lo mínimo posible" que pidió Manuel. Con la
   altura fija aquí y `min-height: 0` en `.content` (el `min-height: 0` es
   necesario porque, si no, un hijo flex nunca se encoge por debajo del
   tamaño de su propio contenido, y el `overflow: auto` de `.content` no
   llegaría a activarse), ahora solo se desplaza el contenido central; la
   barra lateral se queda siempre visible. */
.app-shell {
  display: flex;
  height: 100vh;
}

/* --- Barra lateral de navegación (Bloque 2) --- */

/* Ancho reducido (Bloque 14.5, sexta pieza): Manuel pidió "optimizar mucho
   mejor el espacio en la pantalla" y, en concreto, reducir este selector de
   pestañas — antes 200px con bastante hueco vacío para solo cuatro botones
   de texto cortos. Se reduce el padding lateral a la vez para que el texto
   siga sin recortarse en el nuevo ancho. */
.sidebar {
  width: 140px;
  flex-shrink: 0;
  background: var(--sidebar);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 16px 8px;
  gap: 4px;
  overflow-y: auto;
}

.brand {
  font-weight: 700;
  font-size: 15px;
  padding: 8px 10px 16px;
  color: var(--text);
}

/* Número de versión (15/09/2026, a petición de Manuel: "me gustaria que en
   la app se pusiera un numero de control de versiones"): texto pequeño y
   apagado junto al nombre de la app, no una insignia llamativa -- es una
   referencia para depurar/soporte, no algo que deba llamar la atención.
   Ahora también es un botón (mismo día, a petición de Manuel de tener "un
   espacio para ver las ultimas modificaciones introducidas"): un clic abre
   el modal de Novedades -- ver #novedades-btn más abajo para la otra forma,
   más visible, de llegar a lo mismo. */
.app-version {
  font-weight: 400;
  font-size: 11px;
  color: var(--muted);
  appearance: none;
  border: none;
  background: none;
  padding: 0;
  margin-left: 2px;
  font-family: inherit;
  cursor: pointer;
}

.app-version:hover,
.app-version:focus-visible {
  color: var(--accent);
  text-decoration: underline;
}

/* Novedades (15/09/2026): historial de cambios visible desde la propia
   aplicación -- Manuel: "incluye en la aplicacion un espacio para ver las
   ultimas modificaciones introducidas". Contenido de APP_CHANGELOG (ver
   01-core.js), mostrado dentro de #modal-novedades reutilizando el modal
   genérico de siempre. */
.novedades-entry + .novedades-entry {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}

.novedades-entry h3 {
  margin: 0 0 8px;
  font-size: 15px;
  color: var(--text);
}

.novedades-entry ul {
  margin: 0;
  padding-left: 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.novedades-entry li {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.4;
}

.nav-btn {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--muted);
  text-align: left;
  padding: 10px 8px;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}

.nav-btn:hover {
  background: var(--panel-alt);
  color: var(--text);
}

.nav-btn.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
}

.sidebar-spacer {
  flex: 1;
}

/* Selector de tema (Bloque 14.5, segunda pieza): alterna claro/oscuro y
   guarda la elección en preferencias.json (ver `applyTheme` en app.js). */
.theme-toggle-btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}

.theme-toggle-btn:hover {
  color: var(--text);
}

/* --- Contenido principal --- */

.content {
  flex: 1;
  min-height: 0;
  padding: 20px 24px;
  overflow: auto;
}

.view {
  display: none;
}

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

.view h1 {
  margin: 0 0 6px;
  font-size: 20px;
}

.view-note {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.5;
  max-width: 560px;
}

.test-field {
  display: block;
  max-width: 360px;
  font-size: 13px;
  color: var(--muted);
  margin-top: 8px;
}

.test-field input {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
}

/* --- Guardar / abrir jugada (Bloque 7) --- */

.file-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

.file-status {
  font-size: 13px;
  color: var(--muted);
  min-height: 18px;
}

/* Bloque 14: Guardar/Abrir/Exportar viven ahora en el menú Archivo, no en
   botones por vista; este texto recuerda dónde están y el atajo. */
.file-toolbar-hint {
  font-size: 12px;
  color: var(--muted);
  font-style: italic;
}

.file-status.is-error {
  color: var(--danger-strong);
}

/* Enlace "¿Olvidaste tu contraseña?" / "Volver a entrar" del modal Cuenta
   (13/09/2026, recuperar contraseña) -- un botón que se ve como un enlace
   discreto, no como los botones de acción normales (mode-btn). */
.btn-link-simple {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
  padding: 4px 0;
  display: block;
}

.file-status.is-ok {
  color: var(--success);
}

/* Enlace "Deshacer" de la papelera de 30 segundos (revisión técnica interna,
   29/08/2026, ver offerUndoableRemoval() en app.js) -- dentro del mismo
   .file-status donde ya se mostraba el aviso de "eliminado". */
.undo-link {
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.undo-link:hover {
  color: var(--text);
}

/* Playbook (Bloque 14.5, tercera pieza): franja que sustituye al guardado en
   archivo mientras se edita una copia de jugada dentro del Playbook de un
   equipo (ver jugada-playbook-banner en index.html / app.js). */
.playbook-edit-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  padding: 10px 14px;
  background: var(--panel-alt);
  border: 1px solid var(--accent);
  border-radius: 8px;
  font-size: 13px;
}

.playbook-edit-banner span {
  flex: 1 1 auto;
}

.playbook-edit-banner[hidden] {
  display: none;
}

/* Aviso de próximos partidos (26/08/2026, cuadro de mandos): mismo lenguaje
   visual que .playbook-edit-banner (franja con borde de acento), pero como
   lista de partidos en vez de una sola línea -- ver
   renderProximosPartidosAlert() en app.js. */
.equipo-dashboard-alert {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 10px 0 18px;
  padding: 12px 16px;
  background: var(--panel-alt);
  border: 1px solid var(--accent);
  border-radius: 8px;
}

.equipo-dashboard-alert h3 {
  margin: 0;
  font-size: 14px;
}

.equipo-dashboard-alert[hidden] {
  display: none;
}

.equipo-dashboard-alert-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.equipo-dashboard-alert-item {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: baseline;
  width: 100%;
  font-size: 13px;
  font-family: inherit;
  color: var(--text);
  background: none;
  border: none;
  border-radius: 6px;
  padding: 4px 6px;
  text-align: left;
  cursor: pointer;
}

.equipo-dashboard-alert-item:hover {
  background: var(--panel);
}

.equipo-dashboard-alert-item-fecha {
  font-weight: 600;
  min-width: 110px;
}

.equipo-dashboard-alert-item-liga {
  color: var(--muted);
}

/* Convocatorias agrupadas por liga/competición (26/08/2026), a petición de
   Manuel -- ver renderEquipoConvocatorias() en app.js. Cada liga es ahora
   un grupo plegable ("las ligas se tienen que poder agrupar para que quede
   la pantalla más limpia"), con el mismo lenguaje visual de chevron que
   las tarjetas de partido de dentro (.evaluacion-player-chevron), y dentro
   de cada una los partidos se subagrupan por mes. */
.convocatorias-grupo {
  margin-bottom: 16px;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}

.convocatorias-grupo-toggle {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  background: var(--panel-alt);
  border: none;
  color: var(--text);
  padding: 12px 14px;
  cursor: pointer;
  text-align: left;
  font: inherit;
}

.convocatorias-grupo-toggle:hover {
  background: var(--panel);
}

.convocatorias-grupo.is-expanded .convocatorias-grupo-toggle .evaluacion-player-chevron {
  transform: rotate(90deg);
}

.convocatorias-grupo-title {
  font-size: 15px;
  font-weight: 600;
}

.convocatorias-grupo-meta {
  color: var(--muted);
  font-size: 13px;
}

.convocatorias-grupo-body {
  padding: 14px;
  border-top: 1px solid var(--border);
}

.convocatorias-grupo-body[hidden] {
  display: none;
}

/* Meses plegables dentro de cada liga (26/08/2026), a petición de Manuel:
   "quiero que se puedan agrupar también los partidos de cada mes" -- mismo
   patrón de chevron+toggle que la liga que los contiene, pero un escalón
   más pequeño y sin fondo propio (la liga ya tiene el suyo). */
.convocatorias-mes {
  margin: 10px 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}

.convocatorias-mes:first-child {
  margin-top: 0;
}

.convocatorias-mes-toggle {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  background: none;
  border: none;
  color: var(--text);
  padding: 8px 10px;
  cursor: pointer;
  text-align: left;
  font: inherit;
}

.convocatorias-mes-toggle:hover {
  background: var(--panel-alt);
}

.convocatorias-mes.is-expanded .convocatorias-mes-toggle .evaluacion-player-chevron {
  transform: rotate(90deg);
}

.convocatorias-mes-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.convocatorias-mes-meta {
  color: var(--muted);
  font-size: 12px;
}

.convocatorias-mes-body {
  padding: 10px;
  border-top: 1px solid var(--border);
}

.convocatorias-mes-body[hidden] {
  display: none;
}

/* --- Formulario de ejercicios (Bloque 8) --- */

.exercise-form {
  max-width: 900px;
  margin-top: 16px;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px 16px;
  margin-bottom: 12px;
}

.form-field {
  display: block;
  font-size: 13px;
  color: var(--muted);
}

.form-field input[type='text'],
.form-field textarea {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  resize: vertical;
}

.form-field-wide {
  display: block;
  margin-bottom: 12px;
}

.form-field-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--muted);
  max-width: 900px;
  margin-top: 4px;
}

.form-field-checkbox input {
  width: 16px;
  height: 16px;
}

.court-editor {
  margin-top: 12px;
  /* Columna en vez de bloque (16/09/2026): ahora `.court-editor` tiene dos
     hijos en el flujo normal, `.court-row` y la leyenda de colores
     (`.jugada-graphic-note`) que antes vivía en la columna de texto de la
     izquierda -- ver el comentario junto a esa clase más abajo y en
     index.html. Como columna flex, la leyenda queda debajo de la cancha en
     vez de superpuesta o al lado. */
  display: flex;
  flex-direction: column;
}

/* Layout en dos columnas de Jugadas y Ejercicios (Bloque 14.5, octava
   pieza): Manuel, viendo la sexta/séptima pieza en marcha: "esto me parece
   muy cutre... no hace falta esos campos de textos tan grandes, la parte
   gráfica no la quiero abajo y a la derecha, quiero en menos de la mitad a
   la izquierda los textos y la otra mitad el gráfico". Antes los campos de
   texto (aunque ya plegables desde la séptima pieza) ocupaban todo el ancho
   por encima de la cancha; ahora van en una columna izquierda más estrecha
   y la cancha con su botonera en la columna derecha, uno junto al otro. */
.editor-layout {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  margin-top: 16px;
}

/* Ancho de la columna de texto: una base de 380px que puede encogerse en
   pantallas estrechas pero no crecer más allá de 560px en pantallas muy
   anchas/maximizadas (Bloque 14.5, novena pieza: "no aprovechamos la
   pantalla completa" — antes el tope era 460px, demasiado poco en una
   ventana maximizada), para que el gráfico siempre se lleve la mayor parte
   del ancho pero la columna de texto también crezca y aproveche el espacio,
   sin pasar de la mitad de la pantalla. */
.editor-panel-left {
  flex: 1 1 380px;
  max-width: 560px;
  min-width: 260px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
}

.editor-panel-left > *:first-child {
  margin-top: 0;
}

.editor-panel-right {
  flex: 3 1 620px;
  min-width: 0;
}

.editor-panel-right .court-editor {
  margin-top: 0;
}

/* Ajuste a pantalla completa en Jugadas/Ejercicios (Bloque 18, 09/09/2026):
   Manuel probó de verdad en un iPad con lápiz y reportó selección de
   iconos poco fiable, el lápiz provocando scroll de página al dibujar, y
   pidió "recoger el texto superior, haciendo como dos columnas: en la
   izquierda los textos, en la derecha la parte gráfica", con "los iconos
   junto a la pista" y sin que eso "genere un scroll en el navegador". El
   HTML ya movió el título/instrucciones/toolbar de archivo dentro de
   `.editor-panel-left` (ver index.html) para que la columna derecha
   arranque justo arriba del todo; aquí se encadena una altura acotada
   desde `.view.is-active` (marcada con la clase `view-court-fit`) hasta la
   propia cancha, en vez de dejar que cada nivel crezca según su contenido
   y confiar en el scroll general de `.content` (justo lo que Manuel pidió
   evitar). Solo se aplica a estas dos vistas -- marcadas explícitamente
   con `.view-court-fit` en el HTML -- así que el resto de vistas
   (Equipos, Biblioteca, Calendario...) no cambian nada de su
   comportamiento ni de su scroll habitual. Manuel confirmó que este mismo
   ajuste debe verse igual en Windows, no solo en iPad -- por eso no va
   detrás de ninguna media query de tamaño de pantalla. */
.view.is-active.view-court-fit {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.view-court-fit .editor-layout {
  flex: 1 1 auto;
  min-height: 0;
  /* La base de `.editor-layout` (arriba) usa align-items:flex-start a
     propósito para que cada columna ocupe solo lo que necesita; aquí pasa
     a stretch para que las dos columnas compartan siempre la misma altura
     -- toda la que quede disponible en la vista -- y así ninguna de las
     dos pueda estirar la vista entera por encima de esa altura. */
  align-items: stretch;
}

.view-court-fit .editor-panel-left {
  /* Base de 300px (antes 380px en `.editor-panel-left` general) y suelo de
     200px (antes 260px) -- comprobado con Playwright en un ancho de iPad
     real (834px de viewport en vertical): con los valores de escritorio la
     columna de texto se quedaba en su suelo de 260px y a la cancha+botonera
     solo le quedaban ~366px, un diagrama incómodamente pequeño para dibujar
     con el dedo/lápiz -- y, al haber desactivado el pellizco-zoom de la
     página (ver meta viewport en index.html), no hay ningún zoom de
     respaldo si el diagrama sale pequeño. Con esta base más ajustada la
     cancha gana a sitio siempre que el ancho esté ajustado (iPad), sin
     cambiar casi nada en una ventana de escritorio ancha, donde ambas
     columnas ya llegan sobradas a su tope. */
  flex: 1 1 300px;
  min-width: 200px;
  min-height: 0;
  /* Con el título/instrucciones/toolbar de archivo ahora también dentro de
     esta columna, puede haber más texto del que cabe en la altura
     disponible (sobre todo en Ejercicios, con más campos) -- en vez de
     dejar que desborde y arrastre un scroll de página entera, esta columna
     se desplaza sola, por su cuenta, sin afectar a la columna derecha. */
  overflow-y: auto;
}

.view-court-fit .editor-panel-right {
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.view-court-fit .editor-panel-right .court-editor {
  flex: 1;
  min-height: 0;
}

.view-court-fit .court-row {
  /* Antes `height: 100%` (se quedaba con todo el alto de `.court-editor`,
     que entonces solo tenía este único hijo). Ahora `.court-editor` es una
     columna flex con `.court-row` y, debajo, la leyenda de colores -- así
     que `.court-row` solo debe quedarse con el hueco que le sobra después
     de esa leyenda (`flex: 1 1 auto`), no con el 100% fijo, o la leyenda no
     tendría sitio y se saldría de la vista acotada (Bloque 18). */
  flex: 1 1 auto;
  min-height: 0;
}

/* La cancha ya no solo se limita por ancho (`.court-stack`/`.court-half`/
   `.court-full` de más abajo) sino también por la altura que le deja
   disponible `.court-row` -- si no, en un iPad en horizontal (más ancho
   que alto que en vertical) podría seguir intentando crecer más alta de lo
   que cabe y forzar scroll. `.court-stack` recibe su relación de aspecto
   por JS (`aspect-ratio`, calculado del viewBox en `renderCourt()` --
   Bloque 18, quinta vuelta, 10/09/2026, antes esta proporción intrínseca
   la aportaba el propio `<svg>` de `.court-svg`), así que con `width`/
   `height` ya en juego el navegador reduce ambas dimensiones a la vez para
   no deformarla -- igual que ya hace cualquier <img> con `max-width`/
   `max-height` combinados. */
.view-court-fit .court-stack {
  max-height: 100%;
}

/* La botonera de iconos tiene una altura fija (la suma de sus botones de
   44px); en la inmensa mayoría de tamaños de pantalla cabe entera junto a
   la cancha, pero por si un iPad en horizontal con la barra de Safari
   visible se quedara justo, se le da un scroll propio SOLO a la botonera
   (nunca a toda la página) como último recurso -- así ningún icono queda
   inaccesible del todo, aunque en la práctica no debería llegar a hacer
   falta. */
.view-court-fit .tool-rail {
  max-height: 100%;
  overflow-y: auto;
}

/* --- Sesiones (Bloque 9) --- */

.form-field-title {
  display: block;
  max-width: 500px;
  font-size: 13px;
  color: var(--muted);
  margin-top: 16px;
}

.form-field-title input,
.form-field-title textarea {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
}

.session-add-panel {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-top: 16px;
  max-width: 1000px;
}

.session-add-col {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px;
}

.session-add-col h2 {
  margin: 0 0 8px;
  font-size: 13px;
  color: var(--muted);
}

.session-add-col > .mode-btn {
  width: 100%;
  margin-bottom: 8px;
}

.session-quick-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.session-quick-form input,
.session-quick-form textarea,
.session-quick-form select {
  padding: 7px 9px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  resize: vertical;
}

.session-quick-form .mode-btn {
  margin-top: 2px;
}

.session-summary {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 18px;
  font-size: 14px;
}

.session-summary span#sesion-block-count {
  color: var(--muted);
  font-size: 13px;
}

.session-block-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  max-width: 1000px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.session-block {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
}

.session-block-index {
  font-size: 13px;
  color: var(--muted);
  width: 20px;
  flex-shrink: 0;
}

.session-block-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  flex-shrink: 0;
  text-transform: uppercase;
}

.session-block-badge.badge-exercise {
  background: #2a4d7a;
  color: #eef4fb;
}

.session-block-badge.badge-jugada {
  background: #6a3a1f;
  color: #fbeee4;
}

.session-block-badge.badge-free {
  background: #3a4a2a;
  color: #eef7ea;
}

.session-block-body {
  flex: 1;
  min-width: 0;
}

.session-block-title {
  font-size: 14px;
  font-weight: 600;
}

.session-block-preview {
  font-size: 12px;
  color: var(--muted);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-block-duration {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--muted);
  flex-shrink: 0;
}

.session-block-duration input {
  width: 56px;
  padding: 5px 6px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
}

.session-block-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}

.session-block-actions button {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  padding: 5px 8px;
  font-size: 13px;
  cursor: pointer;
}

.session-block-actions button:hover {
  color: var(--text);
}

.session-block-actions button:disabled {
  opacity: 0.35;
  cursor: default;
}

/* --- Biblioteca (Bloque 10) --- */

.lib-pending-col {
  border-color: var(--accent);
}

.lib-pending-checkbox {
  margin: 2px 0;
}

.library-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  max-width: 1000px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.library-item {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
}

.library-item-badges {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex-shrink: 0;
}

.library-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  text-transform: uppercase;
  text-align: center;
}

.library-badge.badge-exercise {
  background: #2a4d7a;
  color: #eef4fb;
}

.library-badge.badge-jugada {
  background: #6a3a1f;
  color: #fbeee4;
}

.library-badge.badge-sesion {
  background: #4a2a5a;
  color: #f6ecfb;
}

.library-badge.badge-template {
  background: var(--accent);
  color: var(--accent-contrast);
}

.library-item-body {
  flex: 1;
  min-width: 0;
}

.library-item-title {
  font-size: 14px;
  font-weight: 600;
}

.library-item-meta {
  font-size: 12px;
  color: var(--muted);
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.library-item-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.library-item-actions button {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}

.library-item-actions button:hover {
  color: var(--text);
}

/* --- Palabras clave / búsqueda en Biblioteca (Bloque 14) --- */

.library-filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  max-width: 900px;
}

.library-item-keywords {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}

.library-keyword-tag {
  font-size: 11px;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 8px;
}

.mode-selector {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
}

.toolbar-sep {
  width: 1px;
  align-self: stretch;
  background: var(--border);
  margin: 0 4px;
}

.mode-btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  padding: 8px 14px;
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
}

.mode-btn:hover {
  color: var(--text);
}

.mode-btn.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
}

.mode-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

.mode-btn:disabled:hover {
  color: var(--muted);
}

#court {
  width: 100%;
  height: auto;
  touch-action: none;
  border-radius: 8px;
  margin-top: 16px;
  display: block;
}

/* Arreglo (19/08/2026, feedback de Manuel): estas reglas apuntaban a
   "#court", un id que no existe en ningún sitio (el <svg> real tiene
   class="court-svg"), así que nunca se aplicaban y el diagrama de cancha
   crecía sin límite hasta ocupar todo el ancho disponible de la pantalla.
   Con el selector correcto, la media cancha (casi cuadrada, cabe alta) se
   limita más en ancho para no ocupar una pantalla entera de alto; la cancha
   completa es apaisada y no tiene ese problema, así que puede ser más ancha.
   `width: 100%` + `max-width` deja que se achique en pantallas pequeñas sin
   pasarse del tamaño pensado en pantallas grandes. */
.court-svg {
  /* Corrección (Bloque 18, quinta vuelta, 10/09/2026): `display:block`,
     `width:100%` y `height:auto` vivían aquí porque, antes de separar fondo
     y líneas en dos <svg>, este era el único elemento de la pareja y el que
     el `.court-row` (flex) medía para darle tamaño. Ahora ese trabajo lo
     hace `.court-stack` (ver más abajo, con `aspect-ratio` puesto por JS) --
     `.court-svg` y `.court-svg-bg` simplemente rellenan la celda de la
     rejilla que `.court-stack` les da, así que les basta con ocupar el
     100% de esa celda en ambas dimensiones. */
  width: 100%;
  height: 100%;
  /* Evita que Safari en iPad interprete un arrastre con el dedo/lápiz sobre
     la cancha como un gesto de scroll/zoom de página (Bloque 18,
     09/09/2026, feedback de Manuel probando en un iPad real: "el lápiz hace
     scroll al intentar dibujar" y "no se selecciona/deselecciona bien los
     iconos" -- ambos síntomas del mismo problema: el navegador compitiendo
     con el propio arrastre de la app por el mismo gesto). Mismo mecanismo
     que ya usan `.panel-header`/`.modal-header` más abajo para sus propias
     cabeceras arrastrables -- aquí faltaba en la propia cancha, que es
     donde el usuario dibuja/arrastra de verdad. Se aplica a todo el <svg>
     y por tanto a todos los grupos que arrastra dentro (jugadores, zonas,
     texto libre, tiradores de línea/zona), sin tener que repetirlo en cada
     uno. */
  touch-action: none;
}

/* `.court-stack` (Bloque 18, cuarta vuelta, 10/09/2026): envuelve
   `.court-svg-bg` (superficie + textura de parqué) y `.court-svg`
   (líneas/jugadores/zonas, el de siempre) apilándolos uno encima del otro
   con CSS Grid -- los dos ocupan la misma celda (`grid-area: 1 / 1`), así
   que se superponen sin usar `position: absolute` ni tener que calcular a
   mano el tamaño de uno a partir del otro. Se pintan en el orden del propio
   HTML (fondo primero, `.court-svg` después, así que queda encima), como
   con cualquier par de elementos normales -- ver la nota larga junto a
   `courtSkinTextureMarkup` en court.js sobre POR QUÉ hacía falta separarlos
   en dos `<svg>` en vez de dejarlos convivir dentro de uno solo.

   Corrección (Bloque 18, quinta vuelta, 10/09/2026): esta nota decía antes
   que el tamaño real de todo el conjunto lo seguía marcando `.court-svg`
   con sus propias reglas -- ESO ERA INCORRECTO y causaba una regresión real
   (detectada por `panel-arrastrable.test.js`, no a simple vista): un <svg>
   normal tiene relación de aspecto intrínseca (por su viewBox) y por eso
   `width:100%; height:auto` le bastaba para calcular su propia altura, pero
   un <div> de rejilla como `.court-stack` NO tiene ninguna relación de
   aspecto propia, así que con solo `display:grid` colapsaba a un tamaño
   mínimo (la cancha salía a la mitad de su tamaño real). Ahora es
   `.court-stack` quien lleva el `width:100%`, los topes de
   `.court-half`/`.court-full` y `.view-court-fit`'s `max-height`, IGUAL que
   antes llevaba `.court-svg` -- y además recibe un `aspect-ratio` calculado
   por JS a partir del propio viewBox (ver `renderCourt()` en 01-core.js),
   que es lo que le da la relación de aspecto intrínseca que un <div> no
   tiene por sí solo. `.court-svg`/`.court-svg-bg` ya no necesitan marcar
   ningún tamaño propio: simplemente rellenan (100%/100%) la celda que
   `.court-stack` les da. */
.court-stack {
  display: grid;
  width: 100%;
  min-width: 0;
}
.court-stack > svg {
  grid-area: 1 / 1;
}
.court-svg-bg {
  width: 100%;
  height: 100%;
  /* Nunca debe robarle el toque/clic a la cancha interactiva de encima. */
  pointer-events: none;
}

/* Topes ampliados (Bloque 14.5, novena pieza): Manuel, viendo la octava
   pieza maximizada de verdad ("no aprovechamos la pantalla completa"),
   pidió que la cancha también creciera con la ventana. Además, pidió
   aparte que la media pista en concreto se viera "al menos un 25% más"
   grande: al ser casi cuadrada (frente a la completa, apaisada) antes se
   limitaba más para no crecer también en alto sin control, quedando
   proporcionalmente pequeña. Su tope crece más que el de la cancha
   completa (700px, un 25% más que los 560px que le tocarían si mantuviera
   la misma proporción de antes) para que se note claramente más grande sin
   dejar de caber cómoda en alto en una ventana maximizada. */
.court-stack.court-half {
  max-width: 700px;
}

.court-stack.court-full {
  max-width: 1000px;
}

/* Bloque 14.5, cuarta pieza: el rectángulo de `court-surface` ya no termina
   en la línea de banda/fondo real, sino que incluye además la franja de
   fuera de banda/fondo (ver OOB_M en court.js) — por eso ya no lleva su
   propio borde (antes `stroke: #f5f5f5`, que dibujaba un marco alrededor de
   TODA la franja, no de la cancha real). El borde de la cancha de verdad
   ahora es `.court-boundary` (más abajo), dibujado en las coordenadas reales
   de la línea de banda/fondo, no en el borde del rectángulo de superficie. */
.court-surface {
  fill: #c8845a;
}

.court-line {
  fill: none;
  stroke: #f5f5f5;
  stroke-width: 3;
}

/* Borde exterior de la cancha (línea de banda/fondo real, Bloque 14.5,
   cuarta pieza): mismo color que el resto de líneas de cancha, un poco más
   grueso para distinguirlo como el límite de juego (a diferencia de
   `court-line` a secas, usado para las marcas internas: zona, círculos...). */
.court-boundary {
  stroke-width: 4;
}

.court-basket {
  fill: #f5f5f5;
}

.court-backboard {
  stroke-width: 5;
}

/* Herramientas de edición del gráfico (Bloque 14.5, segunda pieza): a
   petición de Manuel, pasan de una fila de botones de texto encima de la
   cancha a una columna estrecha de iconos a la derecha del propio gráfico
   (ver el nuevo `.tool-rail` dentro de `.court-row` en el HTML). El nombre
   completo de cada herramienta sigue disponible como `title` (tooltip al
   pasar el ratón) y como texto para lectores de pantalla. */
.tool-rail {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex-shrink: 0;
}

/* Agrupación visual de los 3 bloques de la botonera (Bloque 14.5, octava
   pieza): Manuel, sobre la botonera de la séptima pieza: "los selectores
   gráficos no están bien agrupados y ordenados". Una línea fina separa cada
   bloque (vista/deshacer-rehacer/colores, colocar elemento, dibujar línea)
   del siguiente, en vez de que los tres queden pegados sin ninguna
   separación visual entre ellos. */
.tool-rail > * + * {
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

/* Bloque 14.5, sexta pieza: Manuel pidió repartir los iconos de herramienta
   en "dos columnas y uno en solo una columna larga" en vez de una única
   columna de 10 botones, para que el conjunto ocupe menos alto junto a la
   cancha (norma general de esta entrega: minimizar el scroll de pantalla).
   Los cinco de "colocar elemento" (mover, atacante, defensor, cono, mano a
   mano) van en una rejilla de 2 columnas; los cinco de "dibujar línea"
   (movimiento/pase/tiro/bote/bloqueo), que se usan de forma más secuencial,
   se quedan en una única columna larga debajo. */
.tool-rail-grid {
  display: grid;
  grid-template-columns: repeat(2, 44px);
  gap: 6px;
}

/* Bloque 14.5, novena pieza: Manuel, viendo la botonera ya agrupada de la
   octava pieza en su pantalla real: "esto tiene que estar a dos columnas"
   (sobre las capturas del grupo de líneas, que aún iba en una única columna
   larga desde la sexta pieza). Pasa al mismo patrón de rejilla de 2 columnas
   que el grupo de "colocar elemento", para que ambos bloques ocupen menos
   alto junto a la cancha. Con 5 iconos en 2 columnas sobra una celda: el
   último botón (Bloqueo) se ensancha a las 2 columnas para no dejar un
   hueco vacío suelto (mismo recurso que `.tool-btn-wide` ya usa en el otro
   grupo). */
.tool-rail-lines {
  display: grid;
  grid-template-columns: repeat(2, 44px);
  gap: 6px;
}

/* Zonas (Bloque 14.5, décima pieza): mismo patrón de rejilla de 2 columnas
   que el resto de grupos de esta botonera; el triángulo (tercer botón,
   impar) se ensancha a las 2 columnas igual que "Bloqueo" en
   .tool-rail-lines, para no dejar una celda vacía suelta. */
.tool-rail-zones {
  display: grid;
  grid-template-columns: repeat(2, 44px);
  gap: 6px;
}

.tool-btn {
  appearance: none;
  /* 44px (antes 42px, Bloque 18, 09/09/2026): Manuel probó en un iPad real
     y reportó que la selección de iconos "no funciona bien" con el dedo/
     lápiz -- 44×44pt es el tamaño mínimo de objetivo táctil que recomienda
     la guía de interfaz de Apple; 42px se quedaba justo por debajo. */
  width: 44px;
  height: 44px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  border-radius: 8px;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
}

.tool-btn svg {
  width: 22px;
  height: 22px;
  stroke: currentColor;
}

.tool-btn:hover {
  color: var(--text);
}

.tool-btn.is-active {
  background: #2a4d7a;
  border-color: #2a4d7a;
  color: #eef4fb;
}

/* Botón "ancho" dentro de una rejilla de 2 columnas (Bloque 14.5, octava
   pieza): ocupa las 2 columnas en vez de dejar una celda vacía y
   descuadrada. Se usa en el botón "Mover / seleccionar" (la herramienta por
   defecto, primero de su grupo) y en "Colores" (último de su grupo, antes
   quedaba solo en una fila con una celda vacía al lado) — al ser el primer
   y el último elemento de cada rejilla respectivamente, la colocación
   automática de CSS Grid ya deja el resto de botones repartidos en un 2×2
   perfecto sin necesidad de reordenar nada en el HTML. */
.tool-btn-wide {
  grid-column: span 2;
  width: auto;
}

/* Modo de cancha, deshacer/rehacer y colores integrados en la propia
   botonera del gráfico (Bloque 14.5, séptima pieza) — antes vivían en una
   fila `.mode-btn` aparte encima de la cancha; deshacer/rehacer necesitan
   el mismo estado deshabilitado que tenían allí. */
.tool-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

.tool-btn:disabled:hover {
  color: var(--muted);
}

/* Texto solo para lectores de pantalla (los botones de herramienta son
   icono + `title`, sin texto visible, para que quepan en la columna
   estrecha junto al gráfico). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* `justify-content` (Bloque 14.5, sexta pieza -> octava pieza): en la sexta
   pieza esto era `flex-end` para "encajar la pista a la derecha" cuando la
   cancha ocupaba sola todo el ancho de la vista. Con el layout en dos
   columnas de la octava pieza (`.editor-layout`), la cancha ya vive en su
   propia columna derecha `.editor-panel-right`; `flex-end` ahí solo abriría
   un hueco vacío entre la columna de texto y la cancha en pantallas anchas,
   así que pasa a `flex-start` para que la cancha quede pegada al borde
   izquierdo de su columna (junto a la columna de texto), no al borde
   derecho de la ventana. */
.court-row {
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 12px;
  position: relative;
}

/* Panel flotante junto al elemento seleccionado (Bloque 14.5, cuarta pieza):
   antes vivía fijo en el hueco a la derecha de la cancha, a petición de
   Manuel — "me gustaría ver las opciones de cambio con unos iconos alrededor
   del cursor y no tener que desplazarlo hasta el hueco a la derecha de la
   pantalla". Un menú circular de iconos alrededor del cursor es poco
   práctico aquí (hay selectores de color e inputs numéricos, no solo
   botones), así que en su lugar el mismo panel de siempre ahora flota junto
   al elemento seleccionado (`positionFloatingPanel()` en app.js, usando
   `getScreenCTM()` para convertir la posición del elemento en la cancha a
   coordenadas de pantalla) en vez de quedarse fijo en el lateral — se sigue
   viendo sin mover la vista, sea cual sea el sitio de la cancha donde esté.
   Ya no ocupa hueco en el `.court-row` (position: absolute), así que la
   cancha no se encoge ni se desplaza al aparecer. */
.player-panel,
.line-panel,
.zone-panel,
.text-panel {
  width: 220px;
  position: absolute;
  z-index: 5;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px;
}

/* Icono de edición (16/09/2026, a petición de Manuel: "la ventana que se
   abre para poder modificarlo cuando seleccionas un elemento es realmente
   molesta ... a lo mejor un icono pequeño anexo"). Seleccionar un elemento
   ya no abre de golpe su panel completo -- solo aparece este icono redondo
   junto a él (misma colocación automática que los paneles de arriba, ver
   positionFloatingPanel() en 01-core.js); hace falta pulsarlo para abrir el
   panel de edición, que entonces se coloca en el mismo sitio. */
.element-edit-btn {
  width: 32px;
  height: 32px;
  position: absolute;
  z-index: 5;
  appearance: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  color: var(--text);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.element-edit-btn:hover {
  background: var(--panel-alt);
}

/* Cabecera del panel flotante con aspa de cerrar (Bloque 14.5, sexta pieza):
   Manuel señaló que el panel no tenía ninguna forma de quitarse de encima
   del gráfico salvo deseleccionar con la herramienta "mover" sobre la propia
   cancha, así que se añade un botón explícito y visible. */
.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
  /* Arrastrable (29/08/2026): Manuel — "todas las ventanas que se generan
     para editar cuando se insertan se tienen que poder mover/desplazar
     donde desee". El cursor de "mover" en la cabecera es la única pista
     visual de que se puede arrastrar (ver makeFloatingPanelDraggable() en
     01-core.js); `touch-action: none` evita que el navegador intente hacer
     scroll/zoom con el propio gesto de arrastre en vez de dejarlo llegar
     como eventos pointermove normales. */
  cursor: move;
  touch-action: none;
}

.panel-header:active {
  cursor: grabbing;
}

.panel-title {
  margin: 0;
  font-size: 14px;
}

.panel-close-btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--muted);
  width: 26px;
  height: 26px;
  line-height: 1;
  border-radius: 6px;
  font-size: 16px;
  cursor: pointer;
  flex: none;
}

.panel-close-btn:hover {
  color: var(--text);
  background: var(--border);
}

.line-color-field {
  /* Columna, no fila (Bloque 14.5, duodécima pieza): con la fila de 9
     círculos básicos + el personalizado (ver `.color-swatch-row` más abajo)
     ya no cabe cómodo un layout de "texto a la izquierda, selector a la
     derecha" en los 220px del panel flotante -- la etiqueta va arriba y la
     fila de colores debajo, a todo el ancho disponible. */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  margin-bottom: 12px;
  font-size: 13px;
  color: var(--muted);
}

.line-color-field input[type='color'] {
  width: 48px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  cursor: pointer;
}

.line-reset-color-btn {
  appearance: none;
  width: 100%;
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--muted);
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 13px;
  cursor: pointer;
  margin-bottom: 12px;
}

.line-reset-color-btn:hover {
  color: var(--text);
}

.panel-field {
  display: block;
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 12px;
}

.panel-field input[type='number'] {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
}

.panel-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
}

.panel-checkbox input {
  width: 16px;
  height: 16px;
}

/* Cuadro de texto libre (Bloque 14.5, decimoquinta pieza): mismo estilo que
   el resto de campos del panel flotante (`.panel-field input[type='number']`
   más arriba), pero para el `<textarea>` de varias líneas del texto. */
.panel-field textarea.text-content {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  resize: vertical;
}

/* Tamaño de letra del texto libre (Bloque 14.5, duodécima pieza): Manuel —
   "en el texto libre me gustaria que se pudiera elegir el tamaño de la
   letra". Mismo tratamiento visual que el resto de campos del panel
   flotante (`.panel-field input[type='number']`/`textarea.text-content` de
   arriba), pero para un `<select>`. */
.panel-field select.text-font-size {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}

/* Negrita/cursiva/subrayado (Bloque 14.5, decimoquinta pieza): Manuel — "¿le
   puedes dar algunas caracteristicas opcional como negrita/cursiva/
   subrrayado?". Tres botones de alternar (no excluyentes entre sí), mismo
   patrón visual que `.rotate-buttons`/`.rotate-btn` de más abajo (girar el
   elemento de mano a mano), con un fondo resaltado cuando la característica
   está activa. */
.text-format-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.text-format-buttons {
  display: flex;
  gap: 6px;
}

.text-format-btn {
  appearance: none;
  width: 32px;
  height: 32px;
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--text);
  border-radius: 6px;
  font-size: 15px;
  cursor: pointer;
}

.text-format-btn:hover {
  background: var(--border);
}

.text-format-btn.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* Girar el elemento de mano a mano (Bloque 14.5, cuarta pieza): dos botones
   de paso (± HAND_OFF_ROTATE_STEP_DEG grados), no un tirador de arrastre —
   solo visible cuando el elemento seleccionado es de tipo mano a mano (ver
   updateSelectionPanel en app.js). */
.rotate-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.rotate-buttons {
  display: flex;
  gap: 6px;
}

.rotate-btn {
  appearance: none;
  width: 32px;
  height: 32px;
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--text);
  border-radius: 6px;
  font-size: 16px;
  cursor: pointer;
}

.rotate-btn:hover {
  color: var(--text);
  background: var(--border);
}

.danger-btn {
  appearance: none;
  width: 100%;
  border: 1px solid #7a2a2a;
  background: #3a1616;
  color: #f5c2c2;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 13px;
  cursor: pointer;
}

.danger-btn:hover {
  background: #4a1c1c;
}

/* --- Jugadores sobre la cancha (Bloque 4) --- */

.player {
  cursor: grab;
}

.player:active {
  cursor: grabbing;
}

.player text {
  /* 17px (Bloque 14.5, duodécima pieza): antes 20px, reducido junto con el
     resto de elementos del editor ("en general el tamaño de los elementos
     mas pequeños"). Sigue coincidiendo con la "X" de los defensores (17px,
     ver `.player.defender .defender-mark` más abajo) — Manuel ya pidió antes
     que ambas notaciones se leyeran igual de grandes, eso no cambia, solo la
     medida común. `fill` aquí es solo un respaldo: el número de un atacante
     lleva su propio color por instancia vía `style=` inline (ver
     `playerMarkup` en court.js, Bloque 14.5, duodécima pieza — antes solo el
     círculo del balón era personalizable, no el número). */
  font-size: 17px;
  font-weight: 600;
  fill: #f5f5f5;
  pointer-events: none;
  user-select: none;
}

.player .hit-area {
  fill: transparent;
  pointer-events: all;
}

.player .selection-ring {
  fill: none;
  stroke: none;
  stroke-width: 2;
  stroke-dasharray: 4 3;
}

.player.is-selected .selection-ring {
  stroke: #ffd166;
}

/* Selección múltiple (16/09/2026, a petición de Manuel: "poder seleccionar
   distintos elementos en una ventana y poder desplazarlos todos juntos a
   otro sitio"): mismo halo/resplandor que `.is-selected`, pero en azul en
   vez de amarillo, para distinguir "este elemento es el que se está
   editando ahora mismo" (amarillo) de "este elemento forma parte de la
   selección múltiple actual" (azul) -- ver multiSelection en 01-core.js. */
.player.is-group-selected .selection-ring {
  stroke: #4da3ff;
}

/* Atacante: sin balón es solo el número (nada de forma detrás), con balón
   aparece también el círculo, tal como pide la notación FIBA/WABC. El
   número usa siempre --color-atacante (con o sin balón, ver `playerMarkup`
   en court.js). El círculo del balón usaba antes ese mismo valor -- ahora
   usa su propia variable, --color-balon (29/08/2026, revisión técnica
   interna), para que se puedan elegir por separado y el número no se
   funda en su propio círculo cuando ambos coincidían. Esta regla es solo
   el respaldo de CSS: en la práctica siempre gana el `style="fill:..."` en
   línea que pone `playerMarkup`. */
.attacker .ball-ring {
  fill: var(--color-balon);
  stroke: #f5f5f5;
  stroke-width: 2;
}

/* Defensor (Bloque 14.5, cuarta pieza): una "X" con el número de a quién
   defiende en subíndice — notación de pizarra que pidió Manuel, en vez del
   cuadrado anterior (`.defender-shape`, ya sin uso mantenido solo por si
   quedara algún dato antiguo con otro marcado). Selector reforzado
   (`.player.defender .defender-mark`, no solo `.defender-mark`) para ganar
   siempre a la regla general `.player text` de más arriba, que si no le
   pondría el color/tamaño de un jugador normal en vez del de defensor.
   Color personalizable (Bloque 14.5) vía --color-defensor. */
.defender-shape {
  fill: var(--color-defensor);
  stroke: #f5f5f5;
  stroke-width: 2;
}

.player.defender .defender-mark {
  font-size: 17px; /* antes 20px (Bloque 14.5, duodécima pieza), ver nota junto a `.player text` */
  font-weight: 700;
  fill: var(--color-defensor);
}

.defender-sub {
  font-size: 10px; /* antes 12px (Bloque 14.5, duodécima pieza) */
}

/* Cono (Bloque 14.5, cuarta pieza): material de entrenamiento colocable en
   la cancha ("faltan los conos", feedback de Manuel). Color personalizable
   vía --color-cono (Manuel pidió también meter los conos en el selector de
   colores — antes era un naranja fijo, sin variable). */
.cone-shape {
  fill: var(--color-cono);
  stroke: #f5f5f5;
  stroke-width: 1.5;
}

.cone-base {
  fill: #7a3d0f;
}

/* Cono plano (29/08/2026): "un cono plano, que se representa con un
   donut" -- un único círculo sin relleno y de trazo grueso, para que el
   propio trazo forme el anillo/donut (el "agujero" central es simplemente
   la parte sin trazo, no hace falta una segunda forma para recortarlo).
   Color aparte del cono normal, vía --color-cono-plano (editable desde el
   panel "🎨 Colores", igual que el resto de elementos). */
.flat-cone-shape {
  fill: none;
  stroke: var(--color-cono-plano);
  stroke-width: 6;
}

/* Balón (15/09/2026, a petición de Manuel: "un balon con icono como
   balon"): elemento colocable de un solo punto, igual que cono/cono plano.
   Color propio vía --color-balon-elemento -- un primer intento reutilizaba
   --color-balon (el círculo de posesión del atacante), pero Manuel pidió
   después "métele administración de colores": su propia entrada
   independiente en el panel "🎨 Colores", igual que cono/cono plano, no un
   color compartido con el círculo del atacante. Las "costuras" (líneas
   rectas + dos curvas) van en un trazo claro fijo (#f5f5f5, igual que el
   resto de detalles sobre relleno oscuro), no personalizable aparte --
   Manuel solo pidió color para "el balón" en conjunto, no para sus
   costuras. */
.ball-shape {
  fill: var(--color-balon-elemento);
  stroke: #f5f5f5;
  stroke-width: 1.5;
}

.ball-seam {
  fill: none;
  stroke: #f5f5f5;
  stroke-width: 1.2;
}

/* Entrenador (15/09/2026, a petición de Manuel: "el entrenador con icono
   como silbato"): elemento colocable de un solo punto, igual que cono/
   balón, pero representando a una persona (mismo tamaño de selección/
   arrastre que atacante/defensor, ver COACH_SELECTION_RING_R... en
   court.js -- en realidad reutiliza SELECTION_RING_R/HIT_AREA_R
   directamente). Color propio vía --color-entrenador (independiente del
   resto, igual que --color-cono/--color-cono-plano). El orificio del
   silbato (coach-hole) y la anilla del cordón (coach-ring) van en el mismo
   trazo/relleno claro fijo (#f5f5f5) que el resto de detalles sobre relleno
   oscuro, sin variable propia (mismo criterio que las costuras del balón,
   arriba). */
.coach-shape {
  fill: var(--color-entrenador);
  stroke: #f5f5f5;
  stroke-width: 1.5;
}

.coach-hole {
  fill: #f5f5f5;
  stroke: none;
}

.coach-ring {
  fill: none;
  stroke: #f5f5f5;
  stroke-width: 1.3;
}

/* Mano a mano (Bloque 14.5, cuarta pieza): ya no es una línea, es un
   elemento colocable de un solo punto (como el cono), dibujado centrado en
   el origen local del <g> y orientado por su propio `rotate()` — ver
   playerMarkup en court.js. Mismo color personalizable de siempre
   (--color-linea-mano-amano, el nombre de la variable no cambia aunque el
   elemento ya no sea una línea, para no romper preferencias guardadas). */
.hand-off-mark {
  fill: none;
  stroke: var(--color-linea-mano-amano);
  stroke-width: 2;
  stroke-linecap: round;
}

/* --- Líneas de acción (Bloque 5, ampliado en el Bloque 14.5) --- */
/* Movimiento: recta o curva + flecha. Pase: discontinua + flecha, siempre
   recta. Tiro (Bloque 14.5, cuarta pieza): trazo corto de tamaño fijo
   rematado en un pequeño "objetivo" (diana), no una flecha larga hasta la
   canasta. Bote: zigzag. Bloqueo: recta o curva + remate perpendicular al
   final. Cada tipo tiene su propio color personalizable (Bloque 14.5). */

.line-shot-target-ring {
  fill: none;
  stroke-width: 2;
}

.line-shot-target-dot {
  stroke: none;
}

.action-line .line-hit {
  fill: none;
  stroke: transparent;
  stroke-width: 16;
  pointer-events: stroke;
  /* "pointer", no "grab" (16/09/2026, a petición de Manuel: "las lineas no
     se puede desplazar tal como estan... pero se deberian poder desplazar
     como están a otro sitio") -- ahora la línea entera se puede arrastrar
     (ver activeLine en 01-core.js), igual que ya podían las zonas/textos. */
  cursor: grab;
}

.action-line:active .line-hit {
  cursor: grabbing;
}

/* El color de cada trazo (`.line-visible`) va como `style="stroke:..."`
   directamente en el propio <path> (ver `resolveLineColor`/`lineMarkup` en
   court.js) — el color por defecto del tipo o, si la línea tiene uno propio
   asignado desde el panel "Línea seleccionada", ese. El valor de aquí abajo
   es solo un respaldo por si algún trazo se quedara sin ese atributo. */
.action-line .line-visible {
  fill: none;
  stroke: #f5f5f5;
  stroke-width: 2.5;
  pointer-events: none;
  /* `stroke-linejoin: round` (Bloque 14.5, décima pieza): la onda del bote
     (dribbleWavePathData en court.js) es en realidad un contorno de muchos
     segmentos rectos muy cortos, no una curva de verdad — con la unión por
     defecto ("miter", en punta) cada uno de esos segmentos se veía como un
     pico afilado, exactamente el aspecto de "zigzag" que Manuel pidió
     corregir ("no es un zigzag, es una onda"). Con las uniones redondeadas,
     el propio trazo redondea esos ángulos y el conjunto se lee como una
     curva lisa. No afecta a las demás líneas (una sola unión recta o una
     curva Bézier de verdad, donde no hay diferencia visible entre unión en
     punta o redondeada). */
  stroke-linejoin: round;
}

/* Punta de flecha abierta (Bloque 14.5, segunda pieza): mismo trazo que el
   resto de la línea, con los extremos redondeados para que la "v" abierta
   se vea limpia en vez de angulosa. */
.action-line .line-arrowhead {
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Selección de una línea: en vez de sustituir su color (lo que taparía el
   color personalizado que se le haya puesto), se añade un halo/resplandor
   alrededor del trazo real, así se ve cuál está seleccionada sin perder de
   vista su color de verdad — importante ahora que cada línea puede llevar
   un color propio editable con el panel "Línea seleccionada". */
.action-line.is-selected .line-visible {
  filter: drop-shadow(0 0 2.5px #ffd166) drop-shadow(0 0 2.5px #ffd166);
}

/* Selección múltiple (16/09/2026): ver el mismo comentario junto a
   `.player.is-group-selected` más arriba. */
.action-line.is-group-selected .line-visible {
  filter: drop-shadow(0 0 2.5px #4da3ff) drop-shadow(0 0 2.5px #4da3ff);
}

/* Tiradores (Bloque 14.5) sobre la línea seleccionada con la herramienta
   "Mover/seleccionar": uno en cada extremo (inicio/fin), arrastrable en
   cualquier tipo de línea, y uno más en el punto de control para curvarla
   (solo en movimiento/bloqueo, que son las que admiten curvarse). Formas
   distintas para no confundirlos: los extremos son cuadrados blancos, el de
   curvar sigue siendo el círculo amarillo de siempre. */
.line-endpoint-handle {
  fill: #fff;
  stroke: #14181f;
  stroke-width: 2;
  cursor: grab;
}

.line-endpoint-handle:active {
  cursor: grabbing;
}

.curve-handle {
  fill: #ffd166;
  stroke: #14181f;
  stroke-width: 2;
  cursor: grab;
}

.curve-handle:active {
  cursor: grabbing;
}

/* --- Zonas (Bloque 14.5, décima pieza) --- */
/* Herramienta para marcar una zona/área de la cancha, a petición de Manuel:
   "quiero un selector para crear una zona ... se podrian seleccionar tres
   formas distintas" (rectángulo, círculo, triángulo). Se dibujan con relleno
   traslúcido (fill-opacity) para leerse como un área de fondo, no como una
   figura sólida que tape lo que haya debajo o encima — el color en sí (tanto
   el relleno como el trazo del contorno) va como `style="fill:...;
   stroke:..."` directamente en el propio elemento (ver `resolveZoneColor`/
   `zoneMarkup` en court.js), igual que en las líneas, así que gana siempre a
   estas reglas de aquí abajo (solo un respaldo por si alguna zona se
   quedara sin ese atributo). */
.action-zone {
  cursor: grab;
}

.action-zone:active {
  cursor: grabbing;
}

.action-zone .zone-visible {
  fill: #000;
  fill-opacity: 0.16;
  stroke-width: 2;
}

.action-zone.is-selected .zone-visible {
  filter: drop-shadow(0 0 2.5px #ffd166) drop-shadow(0 0 2.5px #ffd166);
}

/* Selección múltiple (16/09/2026): ver el mismo comentario junto a
   `.player.is-group-selected` más arriba. */
.action-zone.is-group-selected .zone-visible {
  filter: drop-shadow(0 0 2.5px #4da3ff) drop-shadow(0 0 2.5px #4da3ff);
}

/* --- Texto libre (Bloque 14.5, decimoquinta pieza) --- */
/* Elemento colocable de un único punto (un clic lo coloca, se arrastra y se
   elimina igual que un cono), pero con contenido editable desde el panel
   "Texto seleccionado" — ver createCourtEditor/textMarkup. Sin color propio
   configurable (a diferencia de líneas/zonas/atacante): Manuel solo pidió
   negrita/cursiva/subrayado como características opcionales, así que el
   color se mantiene fijo, oscuro y legible sobre cualquier estilo de pista
   (clásica lisa o con textura de parqué). */
.action-text {
  cursor: grab;
}

.action-text:active {
  cursor: grabbing;
}

.action-text .text-hit-area {
  fill: transparent;
  pointer-events: all;
}

.action-text .text-selection-ring {
  fill: none;
  stroke: none;
  stroke-width: 2;
  stroke-dasharray: 4 3;
  pointer-events: none;
}

.action-text.is-selected .text-selection-ring {
  stroke: #ffd166;
}

/* Selección múltiple (16/09/2026): ver el mismo comentario junto a
   `.player.is-group-selected` más arriba. */
.action-text.is-group-selected .text-selection-ring {
  stroke: #4da3ff;
}

.free-text-box {
  /* `min-height` (no `height`), con el propio `<foreignObject>` en
     `overflow: visible` (ver textMarkup en court.js): un texto corto queda
     centrado dentro del cuadro de tamaño por defecto, pero uno más largo o
     con varias líneas puede crecer más allá de esa altura por defecto sin
     que ninguna parte quede recortada. */
  width: 100%;
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  color: #171717;
  font-size: 14px;
  line-height: 1.3;
  font-family: inherit;
  white-space: pre-wrap;
  word-break: break-word;
  pointer-events: none;
  user-select: none;
}

/* --- Equipos (Bloque 14, punto 3) --- */

.equipos-layout {
  display: grid;
  grid-template-columns: minmax(280px, 380px) 1fr;
  gap: 24px;
  margin-top: 16px;
  align-items: start;
}

.equipos-list-col h2,
.equipos-detail-col h2 {
  font-size: 16px;
  margin: 0 0 10px;
}

.equipos-detail-col h2:not(:first-child) {
  margin-top: 22px;
}

.equipo-item.is-selected {
  border-color: var(--accent);
}

.equipo-item {
  cursor: pointer;
}

.equipo-item-body {
  flex: 1;
  min-width: 0;
}

.equipo-item-title {
  font-size: 14px;
  font-weight: 600;
}

.equipo-item-meta {
  font-size: 12px;
  color: var(--muted);
  margin-top: 2px;
}

.equipo-delete-btn {
  margin-top: 12px;
  border-color: var(--danger);
  color: var(--danger);
}

.equipo-delete-btn:hover {
  color: var(--danger-strong);
}

.roster-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 4px;
  font-size: 13px;
}

.roster-table th {
  text-align: left;
  color: var(--muted);
  font-weight: 600;
  font-size: 12px;
  padding: 4px 8px;
  border-bottom: 1px solid var(--border);
}

.roster-table td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
}

.roster-table tbody tr:last-child td {
  border-bottom: none;
}

/* Acciones de fila como solo icono (Bloque 14, pieza de gestión de
   plantilla): antes "Quitar" era un botón de texto y no había forma de
   editar; a petición de Manuel ("mostar solo una papelera y un lapiz para
   editar") pasan a ser dos botones pequeños solo con el icono, uno al lado
   del otro. */
.roster-actions-cell {
  white-space: nowrap;
}

.roster-icon-btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  width: 28px;
  height: 28px;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.roster-icon-btn + .roster-icon-btn {
  margin-left: 6px;
}

.roster-edit-btn:hover {
  color: var(--text);
  border-color: var(--accent, var(--border));
}

.roster-remove-btn:hover {
  color: var(--danger-strong);
  border-color: var(--danger);
}

.roster-add-form {
  margin-top: 12px;
  max-width: 420px;
}

/* Modal "Añadir partido" (12/09/2026): a diferencia de ".roster-add-form"
   (inputs sueltos con placeholder), aquí cada campo lleva su etiqueta
   encima -- son más campos y menos evidentes solo por el placeholder
   (Jornada/Fecha/Hora/Rival/Lugar/Local-Visitante). */
.partido-new-form label {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 12px;
  color: var(--muted);
}

.partido-new-form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}

/* Botón "Guardar convocatoria y volver" (12/09/2026): ver
   renderEquipoConvocatorias() en 07-calendario.js. */
.convocatoria-guardar-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.roster-empty-row td {
  color: var(--muted);
  font-style: italic;
}

/* Asistencia (nuevo módulo, 24/08/2026): tabla de asistencia por sesión
   (dentro del editor de sesión) y tabla resumen por jugador (dentro de la
   sección "Asistencia" del equipo) -- ambas reutilizan .roster-table, esto
   solo ajusta el ancho del campo de motivo y el aspecto de los checkboxes
   deshabilitados cuando el jugador está marcado como "asistió".
*/
.asistencia-table td {
  vertical-align: middle;
}

.asistencia-table input[type="checkbox"] {
  width: 16px;
  height: 16px;
}

.asistencia-table input[type="text"] {
  width: 100%;
  min-width: 160px;
  box-sizing: border-box;
}

.asistencia-table input:disabled {
  opacity: 0.4;
}

/* --- Sesiones dentro de Equipos (Bloque 14, punto 4) --- */

#equipo-sesion-list {
  margin-top: 10px;
}

.equipo-sesion-item {
  align-items: flex-start;
}

.equipo-sesion-item-body {
  flex: 1;
  min-width: 0;
}

.equipo-sesion-title {
  font-size: 14px;
  font-weight: 600;
}

.equipo-sesion-meta {
  font-size: 12px;
  color: var(--muted);
  margin-top: 2px;
}

.equipo-sesion-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex-shrink: 0;
}

.equipo-sesion-actions button,
.equipo-sesion-transfer-select {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  padding: 5px 8px;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}

.equipo-sesion-actions button:hover {
  color: var(--text);
}

#sesion-editor-back-btn {
  margin-bottom: 12px;
}

.sesion-picker-search {
  display: block;
  width: 100%;
  margin: 8px 0 4px;
  padding: 7px 9px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
}

.sesion-picker-results {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  max-height: 160px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sesion-picker-result {
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  font-size: 12px;
}

.sesion-picker-result:hover {
  border-color: var(--accent);
}

.sesion-picker-result-title {
  font-weight: 600;
  color: var(--text);
}

.sesion-picker-result-meta {
  color: var(--muted);
  margin-top: 1px;
}

.sesion-picker-empty {
  color: var(--muted);
  font-style: italic;
  font-size: 12px;
  padding: 4px 0;
}

/* --- Modales (Bloque 14, quinta pieza) --- */
/* Formularios de "crear/añadir algo" que antes vivían siempre visibles en
   la pantalla (Equipos, Sesiones) y ahora se abren en un cuadro modal para
   que la pantalla de base quede limpia. */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(10, 12, 16, 0.65);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 20px;
}

.modal-overlay[hidden] {
  display: none;
}

.modal-box {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}

/* Variante ancha (29/08/2026), para el modal "Generar propuesta de
   planificación": tres campos de fecha en fila + lista de descansos no
   caben cómodamente en los 420px por defecto. */
.modal-box-wide {
  max-width: 640px;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  /* Ventanas desplazables (Bloque 14, pieza de gestión de plantilla): Manuel
     pidió que "las ventanas para introducir datos en la aplicacion deben
     poder ser desplazadas" — se arrastran por la cabecera (ver
     initModalDragging() en app.js), no por el cuerpo del formulario, para no
     interferir con la selección de texto en los campos. */
  cursor: grab;
  touch-action: none;
  user-select: none;
}

.modal-header:active {
  cursor: grabbing;
}

.modal-header h2 {
  margin: 0;
  font-size: 16px;
}

.modal-close-btn {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 6px;
}

.modal-close-btn:hover {
  color: var(--text);
  background: var(--panel-alt);
}

/* Login obligatorio en la versión web (13/09/2026, a petición de Manuel:
   "quiero que en la app de xbasket tenga el login previo al acceso...
   cualquiera de la calle puede tener acceso") -- ver setUpWebAccessGate()
   (dentro de `if (isWebBuild)`) en 00-platform-web.js. Mismo aspecto que
   .modal-overlay/.modal-box pero con su propia clase, nunca .modal-overlay:
   esa clase ya tiene enganchado en 01-core.js un cierre genérico por
   Escape/clic fuera de la caja para cualquier modal normal, que aquí sería
   justo el agujero por el que colarse sin loguearse. z-index por debajo del
   de .modal-overlay (100) a propósito: si llega el enlace de recuperación
   de contraseña sin haber iniciado sesión todavía, el modal "Cuenta" (con
   la pantalla "Nueva contraseña") tiene que verse POR ENCIMA de este
   overlay, no debajo.
   Fondo sólido, no traslúcido (13/09/2026, ajuste a petición de Manuel: "no
   me gusta que el login de xbasket se haga con una ventana en la qua se
   puede ver la aplicacion por detras, se tiene que hacer sobre una pantalla
   en negro como pasa en x-p") -- x-p resuelve esto mostrando el login como
   una vista de página completa más (nunca hay nada debajo que se pueda
   entrever), y aquí se imita el mismo efecto usando var(--bg) -- el mismo
   fondo sólido de base de toda la app -- en vez de un negro traslúcido
   encima del contenido ya renderizado. Antes de iniciar sesión no se han
   cargado preferencias (bloqueadas por el propio gate, ver gateReady en
   00-platform-web.js), así que el tema sigue en el valor por defecto
   (oscuro), igual que la pantalla de login de x-p. */
.web-access-gate {
  position: fixed;
  inset: 0;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 90;
  padding: 20px;
}

.web-access-gate[hidden] {
  display: none;
}

.web-access-gate-box {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  padding: 20px;
}

.web-access-gate-box h2 {
  margin: 0 0 8px;
}

.modal-body {
  padding: 16px;
}

/* --- Calentamiento (Bloque 14, quinta pieza) --- */
/* Un calentamiento tipo por equipo (temporada) que se copia a cada sesión
   nueva y se puede ajustar por sesión sin afectar al de temporada. */

.calentamiento-item-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  max-width: 500px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.calentamiento-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  font-size: 13px;
}

.calentamiento-item-title {
  flex: 1;
  min-width: 0;
}

.calentamiento-item-duration {
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
}

.calentamiento-item-remove-btn {
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  padding: 3px 8px;
  font-size: 11px;
  cursor: pointer;
}

.calentamiento-item-remove-btn:hover {
  color: var(--danger-strong);
  border-color: var(--danger);
}

.calentamiento-empty {
  color: var(--muted);
  font-style: italic;
  font-size: 12px;
  padding: 4px 0;
}

/* --- Título/descripción y gráficos (pasos) de una jugada (19/08/2026) ---
   Feedback de Manuel: la jugada no tenía forma de ponerle nombre ni de
   explicar en qué consiste, y solo admitía un diagrama. Ahora una jugada
   tiene título + descripción general, y una lista de "gráficos" (pasos),
   cada uno con su propio diagrama y su propia nota; el editor de cancha de
   siempre muestra el gráfico activo, y las pestañas de abajo cambian cuál. */

/* Campos de texto contraíbles (Bloque 14.5, séptima pieza): Manuel pidió que
   "los campos de texto deben poder contraerse... para que la parte gráfica
   sea la fundamental y no tengamos que hacer tanto scroll". Se usa el
   elemento nativo <details>/<summary> (ver index.html) en vez de un
   acordeón propio en JS — el navegador ya gestiona el plegado, el estado
   abierto/cerrado y la accesibilidad de teclado sin código adicional. */
.collapsible-fields {
  margin-top: 16px;
}

.collapsible-fields summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel-alt);
  list-style: none;
}

.collapsible-fields summary::-webkit-details-marker {
  display: none;
}

.collapsible-fields summary::before {
  content: '▸';
  display: inline-block;
  margin-right: 8px;
  transition: transform 0.15s ease;
}

.collapsible-fields[open] summary::before {
  transform: rotate(90deg);
}

.collapsible-fields summary:hover {
  color: var(--text);
}

.jugada-meta {
  max-width: 700px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 16px;
}

.jugada-graphics-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
}

/* Bloque 14.5, novena pieza: en Jugadas esta franja de pestañas nunca lleva
   el atributo `hidden` (siempre hay al menos un gráfico), pero en
   Ejercicios sí se oculta/muestra según el checkbox "Incluir diagrama..."
   — sin esta regla, `display: flex` de arriba gana por especificidad de
   autor sobre el `[hidden]{display:none}` del user-agent y la franja se ve
   igual estando "oculta" (mismo tipo de bug de cascada ya documentado antes
   en este proyecto para otros paneles, ver `.rotate-field` en app.js). */
.jugada-graphics-tabs[hidden] {
  display: none;
}

.jugada-graphic-tab {
  display: flex;
  align-items: center;
  gap: 6px;
  appearance: none;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  padding: 6px 8px 6px 12px;
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
}

.jugada-graphic-tab:hover {
  color: var(--text);
}

.jugada-graphic-tab.is-active {
  background: #2a4d7a;
  border-color: #2a4d7a;
  color: #eef4fb;
  font-weight: 600;
}

/* Iconos de una pestaña de gráfico (renombrar/mover/quitar): mismo estilo
   compacto para los tres, antes solo existía para "quitar" -- ver
   renderJugadaGraphicsTabs()/renderExGraphicsTabs() en 02-jugadas.js /
   03-ejercicios.js (15/09/2026, a petición de Manuel de poder reordenar y
   renombrar los gráficos). */
.jugada-graphic-tab-icon {
  appearance: none;
  border: none;
  background: transparent;
  color: inherit;
  opacity: 0.7;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 4px;
}

.jugada-graphic-tab-icon:hover {
  opacity: 1;
}

.jugada-graphic-add-btn {
  appearance: none;
  border: 1px dashed var(--muted);
  background: transparent;
  color: var(--muted);
  padding: 6px 14px;
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
}

.jugada-graphic-add-btn:hover {
  color: var(--text);
  border-color: var(--accent);
}

.jugada-graphic-note {
  max-width: 700px;
  margin-top: 12px;
}

/* Ahora vive bajo la cancha, dentro de `.court-editor` (16/09/2026, ver el
   comentario en index.html) -- columna de la derecha, más ancha que la de
   texto donde vivía antes, así que ya no hace falta el `max-width: 700px`
   de la regla base (aquí puede aprovechar todo el ancho disponible). Como
   `.court-editor` tiene una altura acotada (Bloque 18, para caber sin
   scroll de página en iPad), esta leyenda no debe crecer sin límite y
   dejar sin sitio a la cancha de encima -- `flex: 0 0 auto` para no
   robarle espacio a `.court-row` salvo el que necesite hasta este tope, y
   su propio scroll si aun así no cabe (varias líneas ampliadas a la vez). */
.view-court-fit .jugada-graphic-note {
  max-width: none;
  flex: 0 0 auto;
  max-height: 220px;
  overflow-y: auto;
}

/* Igual que `.jugada-graphics-tabs[hidden]` más arriba: `.form-field` fija
   `display: block`, así que sin esta regla el campo de nota de Ejercicios
   (el único `.form-field` que usa `hidden` condicionalmente) se seguiría
   viendo aunque "Incluir diagrama..." esté desmarcado. */
.jugada-graphic-note[hidden] {
  display: none;
}

/* Leyenda de colores del gráfico (Bloque 14.5, decimocuarta pieza): Manuel —
   "las notas en las jugadas o en los ejercicios va por colores, cada color
   significa un conjunto de acciones (...) un cuadro con la selección de un
   color, y a continuación por separado el texto (...) cuantas líneas (color
   + texto) necesite". Sustituye al antiguo textarea de texto libre por una
   lista de filas color+texto, tantas como se quiera. */
.graphic-notes-label {
  display: block;
  margin-bottom: 8px;
}

.graphic-notes-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.graphic-note-row {
  display: flex;
  /* `flex-start` (antes `center`, 16/09/2026): con el texto ahora en un
     <textarea> ampliable (ver más abajo), una fila cuyo texto se ha
     arrastrado más alto ya no debe recentrar verticalmente el cuadro de
     color ni el botón de quitar -- quedan arriba, junto a la primera línea
     del texto, en vez de flotar en mitad de una caja más alta. */
  align-items: flex-start;
  gap: 8px;
}

.graphic-note-row .graphic-note-color {
  flex: 0 0 auto;
  width: 40px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  cursor: pointer;
}

/* `.form-field input[type='text']` (más arriba) fija margin-top:6px, pensado
   para un único campo bajo su etiqueta; aquí el texto va en fila junto al
   color, así que se anula ese margen. Misma especificidad que esa regla
   (clase + tipo + clase) para que gane por venir después en la hoja, no por
   casualidad de especificidad.
   Ahora es un <textarea> (16/09/2026, "quiero que esas lineas sean
   ampliables porque los textos son largos"), no un <input> -- de una sola
   línea por defecto (misma altura de 32px que el cuadro de color y el
   botón de quitar, para que la fila arranque igual que antes), pero con
   `resize: vertical` para poder arrastrar el tirador de la esquina y verlo
   más alto cuando el texto no cabe en una línea. `overflow-x: hidden`
   evita un scroll horizontal feo mientras se escribe una palabra larga
   antes de hacer scroll de línea. */
.graphic-note-row textarea.graphic-note-text {
  /* El resto (borde, fondo, color, radio, padding, resize:vertical) ya lo
     pone la regla general `.form-field textarea` de más arriba -- solo se
     ajusta aquí lo que de verdad cambia en esta fila. */
  flex: 1 1 auto;
  width: auto;
  margin-top: 0;
  height: 32px;
  min-height: 32px;
  line-height: 1.4;
  overflow-x: hidden;
}

.graphic-note-remove-btn {
  flex: 0 0 auto;
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  width: 32px;
  height: 32px;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.graphic-note-remove-btn:hover {
  color: var(--danger-strong);
  border-color: var(--danger);
}

.graphic-notes-add-btn {
  margin-top: 8px;
  appearance: none;
  border: 1px dashed var(--muted);
  background: transparent;
  color: var(--muted);
  padding: 6px 14px;
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
}

/* Objetivos de la sesión (29/08/2026): misma fila flex que .graphic-note-row
   de arriba (texto + controles + botón de quitar), con dos controles extra
   propios (el check "Conseguido" y el desplegable de valoración 1-5
   opcional) en vez de un único selector de color. */
.sesion-objetivo-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}

.sesion-objetivo-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.sesion-objetivo-row input.sesion-objetivo-text {
  flex: 1 1 auto;
  margin-top: 0;
}

.sesion-objetivo-conseguido-label {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}

.sesion-objetivo-valoracion {
  flex: 0 0 auto;
  width: 108px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  font-size: 13px;
}

.sesion-objetivo-remove-btn {
  flex: 0 0 auto;
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  width: 32px;
  height: 32px;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.sesion-objetivo-remove-btn:hover {
  color: var(--danger-strong);
  border-color: var(--danger);
}

/* Evaluación de macrociclos (29/08/2026): subsección dentro de cada tarjeta
   de macrociclo expandida en "Planificación de temporada", calculada "de
   abajo hacia arriba" a partir de los objetivos marcados en las sesiones de
   entrenamiento reales (ver computeMacrocicloEvaluacion() en app.js) -- no
   un dato que se introduzca a mano aquí, salvo la reflexión final. */
.macrociclo-evaluacion {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px dashed var(--border);
}

.macrociclo-evaluacion h3 {
  margin: 0 0 6px;
  font-size: 15px;
}

.macrociclo-evaluacion h4 {
  margin: 14px 0 4px;
  font-size: 13px;
  color: var(--muted);
}

.macrociclo-evaluacion-resumen {
  font-size: 14px;
  font-weight: 600;
}

.macrociclo-evaluacion-table {
  margin-top: 8px;
  width: 100%;
}

.macrociclo-evaluacion-table td,
.macrociclo-evaluacion-table th {
  text-align: center;
}

.macrociclo-evaluacion-table td:first-child,
.macrociclo-evaluacion-table th:first-child {
  text-align: left;
}

.graphic-notes-add-btn:hover {
  color: var(--text);
  border-color: var(--accent);
}

/* --- Cuadro de mandos de Equipos (19/08/2026) ---
   Feedback de Manuel: dentro de un equipo estaba "todo mezclado en una
   misma pantalla" (datos, plantilla, calentamiento, sesiones, uno detrás de
   otro). Al entrar en un equipo se ve ahora un cuadro de mandos con una
   tarjeta por apartado; pulsar una tarjeta lleva a esa sección concreta con
   su propio botón "Volver", en vez de tenerlo todo junto y con scroll. */

.equipo-dashboard-title {
  margin: 4px 0 16px;
  font-size: 20px;
}

.equipo-dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  max-width: 900px;
}

.equipo-dashboard-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  appearance: none;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  border-radius: 12px;
  padding: 16px;
  text-align: left;
  cursor: pointer;
}

/* Bloque 16: la tarjeta "Compartir equipo" se oculta con el atributo
   [hidden] mientras no haya sesión iniciada (ver actualizarUIAuth en
   app.js) — sin esta regla, el "display: flex" de arriba (misma
   especificidad que el [hidden] del navegador, pero definido en la propia
   hoja de estilos) ganaría y la tarjeta se vería igualmente. */
.equipo-dashboard-card[hidden] {
  display: none;
}

.equipo-dashboard-card:hover {
  border-color: var(--accent);
}

.equipo-dashboard-card-icon {
  font-size: 22px;
}

.equipo-dashboard-card-title {
  font-size: 15px;
  font-weight: 600;
}

.equipo-dashboard-card-meta {
  font-size: 13px;
  color: var(--muted);
}

/* Bloque 16 (seguimiento): fila del botón "Sincronizar ahora", debajo de
   las tarjetas del cuadro de mandos. */
.equipo-sync-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
}

/* Igual que con .equipo-dashboard-card[hidden]: sin esta regla, el "display:
   flex" de arriba (misma especificidad que el [hidden] del navegador, pero
   definido en la propia hoja de estilos) ganaría y la fila se vería aunque
   no haya sesión iniciada. */
.equipo-sync-row[hidden] {
  display: none;
}

.equipo-sync-meta {
  font-size: 13px;
  color: var(--muted);
}

.equipo-section-back-btn {
  margin-bottom: 16px;
}

/* --- Panel de colores personalizables (Bloque 14.5) --- */

.color-prefs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 10px;
  margin: 14px 0;
}

.color-field {
  /* Columna, no fila (Bloque 14.5, duodécima pieza): mismo motivo que
     `.line-color-field` de arriba -- con la fila de 9 círculos básicos +
     el personalizado ya no cabe un layout de "texto a un lado, selector al
     otro" en las columnas de `.color-prefs-grid` (190px mínimo). */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  font-size: 13px;
  color: var(--text);
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
}

.color-field input[type='color'] {
  width: 36px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
}

/* Selector de color en 9 básicos + personalizado (Bloque 14.5, duodécima
   pieza): Manuel -- "vamos a cambiar la gestion de los colores, ahora habra
   9 colores basicos, dejando un boton por si queremos introducir un color
   personalizado". Fila que envuelve cada `<input type="color">` marcado con
   `js-color-picker` (ver `setupColorSwatchPicker` en app.js): 9 círculos de
   color fijo más el propio input nativo al final, restilado como un décimo
   círculo de borde discontinuo para distinguirlo como "personalizado" sin
   necesitar icono ni texto aparte. `flex-wrap` dejar que se acomode en el
   ancho disponible tanto en el panel flotante (220px) como en la rejilla más
   ancha del modal "Colores". */
.color-swatch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.color-swatch {
  appearance: none;
  width: 20px;
  height: 20px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid var(--border);
  cursor: pointer;
}

.color-swatch:hover {
  border-color: var(--text);
}

/* Resalta cuál de los 9 coincide con el valor actual (ver
   `syncColorSwatchActive` en app.js) -- mismo color de acento amarillo que ya
   usa el resto del editor para marcar selección (tiradores de curva,
   halo de línea/zona seleccionada). */
.color-swatch.is-active {
  border-color: #ffd166;
  box-shadow: 0 0 0 2px #ffd166 inset;
}

/* El input nativo, ya reparentado dentro de `.color-swatch-row` (ver
   `setupColorSwatchPicker`), pasa a verse como un círculo pequeño más entre
   los 9 básicos -- el borde discontinuo (vía los pseudo-elementos de más
   abajo, específicos de Chromium/Electron, el único motor donde corre esta
   app tanto en pantalla como al exportar a PDF) es lo único que lo distingue
   como "el botón de color personalizado" frente a los 9 de color fijo.
   Selector reforzado con `.color-swatch-row` por delante para ganar siempre
   a `.color-field input[type='color']`/`.line-color-field input[type=
   'color']` (misma especificidad que estas, así que sin el refuerzo el
   orden en la hoja de estilos decidiría por casualidad). */
.color-swatch-row input.color-swatch-custom[type='color'] {
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  cursor: pointer;
}

.color-swatch-row input.color-swatch-custom::-webkit-color-swatch-wrapper {
  padding: 0;
}

.color-swatch-row input.color-swatch-custom::-webkit-color-swatch {
  border-radius: 50%;
  border: 2px dashed var(--muted);
}

.modal-section-title {
  margin: 18px 0 8px;
  font-size: 14px;
  color: var(--text);
}

/* Estilo de pista (Bloque 14.5, tercera pieza): cuatro botones con una
   miniatura de listón de parqué a modo de vista previa, para elegir entre
   la pista clásica y los tres parqués de referencia de Manuel. Los
   `background-image` de abajo son la misma copia literal de sus
   `repeating-linear-gradient` que usa el `<foreignObject>` de court.js (no
   una reinterpretación), así que la miniatura es fiel a lo que se ve luego
   en la cancha real. */
.pista-skin-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 10px;
  margin: 10px 0 4px;
}

.pista-skin-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px;
  font-size: 12px;
  color: var(--text);
  background: var(--bg);
  border: 2px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
}

.pista-skin-btn.is-active {
  border-color: var(--accent);
}

.pista-skin-swatch {
  display: block;
  width: 100%;
  height: 40px;
  border-radius: 5px;
}

.pista-skin-clasico {
  background: #c8845a;
}

.pista-skin-azul-naranja,
.pista-skin-gris-cemento,
.pista-skin-roble-clasico {
  /* Las tres capas de veta de madera, copiadas literalmente de los HTML de
     referencia de Manuel (ver WOOD_GRAIN_LAYER_1/2/3 en court.js) — solo
     cambia el color base de cada estilo. */
  background-image:
    repeating-linear-gradient(0deg, rgba(0,0,0,0.035) 0px, rgba(0,0,0,0.035) 6px, transparent 6px, transparent 37.5px),
    repeating-linear-gradient(0deg, rgba(255,255,255,0.03) 0px, rgba(255,255,255,0.03) 9px, transparent 9px, transparent 112.5px),
    repeating-linear-gradient(0deg, rgba(255,255,255,0.05) 0px, rgba(255,255,255,0.05) 1.5px, transparent 1.5px, transparent 7px, rgba(0,0,0,0.04) 7px, rgba(0,0,0,0.04) 8.5px, transparent 8.5px, transparent 15px, rgba(255,255,255,0.03) 15px, rgba(255,255,255,0.03) 16px, transparent 16px, transparent 24px, rgba(0,0,0,0.035) 24px, rgba(0,0,0,0.035) 25px, transparent 25px, transparent 37.5px),
    repeating-linear-gradient(0deg, rgba(0,0,0,0.03) 0px, rgba(0,0,0,0.03) 4px, transparent 4px, transparent 19px, rgba(255,255,255,0.027) 19px, rgba(255,255,255,0.027) 22px, transparent 22px, transparent 41px),
    repeating-linear-gradient(0deg, rgba(0,0,0,0.3) 0px, rgba(0,0,0,0.3) 1px, rgba(255,255,255,0.07) 1px, rgba(255,255,255,0.07) 2.5px, transparent 2.5px, transparent 37.5px),
    repeating-linear-gradient(0deg, rgba(255,255,255,0.055) 0px, rgba(255,255,255,0.055) 37.5px, rgba(0,0,0,0.05) 37.5px, rgba(0,0,0,0.05) 75px, transparent 75px, transparent 112.5px, rgba(255,255,255,0.03) 112.5px, rgba(255,255,255,0.03) 150px, rgba(0,0,0,0.07) 150px, rgba(0,0,0,0.07) 187.5px, transparent 187.5px, transparent 262.5px),
    radial-gradient(70% 85% at 50% 42%, rgba(255,255,255,0.05), rgba(0,0,0,0.1) 92%);
  background-size: 100% 262.5px, 100% 337.5px, auto, auto, auto, auto, auto;
}

.pista-skin-azul-naranja {
  background-color: #c6a672;
}

.pista-skin-gris-cemento {
  background-color: #8f9095;
}

.pista-skin-roble-clasico {
  background-color: #a98a5d;
}

/* --- Evaluación de jugadores (Bloque 14.5, cuarta pieza) ---
   Colores de los 5 niveles de valoración, mismo orden que la leyenda de la
   maqueta que envió Manuel (Excelente/Consolidado/En desarrollo/Necesita
   apoyo/No observado): azul, verde, ámbar, terracota y un punto hueco para
   "sin valorar". Se definen como variables para no repetir el hex en cada
   selector de abajo (leyenda, botones de la ficha y valor del informe). */
:root {
  --eval-excelente: #3a66b0;
  --eval-consolidado: #2f8f5e;
  --eval-desarrollo: #c98a1f;
  --eval-apoyo: #bd5a34;
}

.evaluacion-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin: 10px 0 16px;
  font-size: 13px;
  color: var(--muted);
}

.evaluacion-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.evaluacion-dot {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--border);
}

.evaluacion-dot-excelente {
  background: var(--eval-excelente);
  border-color: var(--eval-excelente);
}

.evaluacion-dot-consolidado {
  background: var(--eval-consolidado);
  border-color: var(--eval-consolidado);
}

.evaluacion-dot-desarrollo {
  background: var(--eval-desarrollo);
  border-color: var(--eval-desarrollo);
}

.evaluacion-dot-apoyo {
  background: var(--eval-apoyo);
  border-color: var(--eval-apoyo);
}

.evaluacion-dot-no_observado {
  background: transparent;
}

.evaluacion-categoria {
  margin-bottom: 18px;
}

.evaluacion-indicador-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.evaluacion-indicador-label {
  flex: 1 1 200px;
  min-width: 160px;
  font-size: 13px;
}

.evaluacion-dots {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
}

/* Botón de nivel de la ficha (editable): mismo círculo que `.evaluacion-dot`
   de la leyenda pero como <button>, hueco hasta que se selecciona (se
   rellena con su color al marcarlo) — igual que los radios de la maqueta. */
.evaluacion-dot-btn {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid var(--border);
  background: transparent;
  cursor: pointer;
  padding: 0;
}

.evaluacion-dot-btn.evaluacion-dot-excelente {
  border-color: var(--eval-excelente);
  background: transparent;
}

.evaluacion-dot-btn.evaluacion-dot-consolidado {
  border-color: var(--eval-consolidado);
  background: transparent;
}

.evaluacion-dot-btn.evaluacion-dot-desarrollo {
  border-color: var(--eval-desarrollo);
  background: transparent;
}

.evaluacion-dot-btn.evaluacion-dot-apoyo {
  border-color: var(--eval-apoyo);
  background: transparent;
}

.evaluacion-dot-btn.evaluacion-dot-no_observado {
  border-color: var(--border);
}

.evaluacion-dot-btn.evaluacion-dot-excelente.is-selected {
  background: var(--eval-excelente);
}

.evaluacion-dot-btn.evaluacion-dot-consolidado.is-selected {
  background: var(--eval-consolidado);
}

.evaluacion-dot-btn.evaluacion-dot-desarrollo.is-selected {
  background: var(--eval-desarrollo);
}

.evaluacion-dot-btn.evaluacion-dot-apoyo.is-selected {
  background: var(--eval-apoyo);
}

.evaluacion-dot-btn.evaluacion-dot-no_observado.is-selected {
  background: var(--muted);
}

.evaluacion-observacion-input {
  flex: 1 1 220px;
  min-width: 180px;
  resize: vertical;
  font-size: 12px;
  padding: 5px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
}

.evaluacion-sub-list {
  margin: 0 0 14px;
}

/* Tarjeta plegable por jugador (Bloque 14.5, cuarta pieza): antes cada
   jugador de la plantilla mostraba siempre, sin poder plegarse, el botón
   "+ Nueva evaluación" y la lista completa de fichas guardadas — con una
   plantilla de más de doce jugadores (feedback de Manuel: "en una plantilla
   de mas de doce jugadores saldria demasiado largo... evalualo y propon la
   forma de que sea todo mucho mas compacto") el listado se hacía
   kilométrico. Ahora cada jugador es una fila compacta de una sola línea
   (nombre + nº de evaluaciones) que se despliega al pulsarla — "abriendo un
   árbol", como sugirió el propio Manuel entre las opciones que planteó. */
.evaluacion-player-card {
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  overflow: hidden;
}

.evaluacion-player-toggle {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  background: none;
  border: none;
  color: var(--text);
  padding: 10px 12px;
  cursor: pointer;
  text-align: left;
  font: inherit;
}

.evaluacion-player-toggle:hover {
  background: var(--panel-alt);
}

.evaluacion-player-chevron {
  display: inline-block;
  width: 12px;
  flex-shrink: 0;
  color: var(--muted);
  transition: transform 0.15s ease;
}

.evaluacion-player-card.is-expanded .evaluacion-player-chevron {
  transform: rotate(90deg);
}

.evaluacion-player-body {
  padding: 0 12px 12px;
  border-top: 1px solid var(--border);
}

/* Se oculta explícitamente con [hidden] además del `display` de arriba: sin
   este selector, la regla `.evaluacion-player-body { padding: ...; border-
   top: ... }` (misma especificidad que `[hidden]`, pero declarada después en
   la hoja de estilos) ganaría al atributo `hidden` que pone app.js y el
   cuerpo se vería aunque estuviera plegado — el mismo tipo de bug de
   cascada ya documentado antes en este proyecto para otros paneles con
   `[hidden]`. */
.evaluacion-player-body[hidden] {
  display: none;
}

/* --- Informe de evaluación (solo lectura, para imprimir/PDF) --- */

.evaluacion-informe-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  border-bottom: 2px solid var(--border);
  padding-bottom: 10px;
  margin-bottom: 14px;
}

.evaluacion-informe-header h2 {
  margin: 0 0 2px;
}

.evaluacion-informe-header p {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.evaluacion-informe-meta {
  text-align: right;
  font-size: 13px;
  color: var(--muted);
}

.evaluacion-informe-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}

.evaluacion-informe-categoria h3 {
  font-size: 14px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 4px;
  margin: 14px 0 6px;
}

.evaluacion-informe-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.evaluacion-informe-table td {
  padding: 3px 0;
}

.evaluacion-informe-valor {
  text-align: right;
  color: var(--muted);
}

.evaluacion-informe-freetext {
  margin-top: 14px;
}

.evaluacion-informe-freetext h3 {
  font-size: 14px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 4px;
  margin: 0 0 6px;
}

.evaluacion-informe-freetext p {
  font-size: 13px;
  white-space: pre-wrap;
  margin: 0;
}

/* Imprimir/"guardar en PDF" (botón 🖶 del informe): truco estándar de solo
   imprimir un elemento — se oculta todo el documento salvo el contenido del
   informe, que pasa a ocupar toda la página. */
@media print {
  body * {
    visibility: hidden;
  }
  .evaluacion-informe-print,
  .evaluacion-informe-print * {
    visibility: visible;
  }
  .evaluacion-informe-print {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    color: #111;
  }
}

/* --- Planificación de temporada (Bloque 14.5, séptima pieza) ---
   Módulo nuevo en Equipos para el arranque de temporada. Reutiliza los
   mismos patrones visuales ya establecidos en Evaluación (tarjetas
   plegables `.evaluacion-player-*`) y en los campos de formulario
   (`.form-field-title`), en vez de inventar un sistema nuevo. */

.planificacion-fechas {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.planificacion-wide-field {
  max-width: none;
  width: 100%;
}

.planificacion-field-narrow {
  max-width: 160px;
}

.planificacion-macrociclo-card {
  flex-direction: column;
  align-items: stretch;
  padding: 0;
  overflow: hidden;
  margin-bottom: 10px;
}

/* Fila de botones sobre la lista de macrociclos (29/08/2026): "Generar
   propuesta…" junto a "+ Añadir macrociclo", en vez de uno debajo del otro. */
.planificacion-macrociclos-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
}

/* Periodos de descanso del modal "Generar propuesta de planificación"
   (29/08/2026): mismo patrón de fila que .sesion-objetivo-row (texto +
   controles + botón de quitar), con dos fechas en vez de un check/select. */
.propuesta-descanso-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}

.propuesta-descanso-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.propuesta-descanso-row input.propuesta-descanso-nombre {
  flex: 1 1 auto;
  margin-top: 0;
}

.propuesta-descanso-row input.propuesta-descanso-inicio,
.propuesta-descanso-row input.propuesta-descanso-fin {
  flex: 0 0 auto;
}

.propuesta-descanso-remove-btn {
  flex: 0 0 auto;
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: 6px;
  width: 32px;
  height: 32px;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.propuesta-descanso-remove-btn:hover {
  color: var(--danger-strong);
  border-color: var(--danger);
}

.propuesta-generar-row {
  margin-top: 16px;
}

.planificacion-macrociclo-fields {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.planificacion-sesiones-tipo-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  list-style: none;
  margin: 10px 0;
  padding: 0;
}

.planificacion-sesion-tipo-item {
  display: flex;
  gap: 8px;
  align-items: center;
}

.planificacion-sesion-tipo-item input {
  flex: 1;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
}

.planificacion-remove-btn,
.planificacion-add-row-btn {
  background: var(--panel-alt);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 5px 10px;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}

.planificacion-remove-btn:hover,
.planificacion-add-row-btn:hover {
  background: var(--panel);
}

.planificacion-add-row-btn {
  margin-top: 8px;
}

.planificacion-grid-wrap {
  margin-top: 12px;
  overflow-x: auto;
}

.planificacion-grid {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.planificacion-grid th,
.planificacion-grid td {
  border: 1px solid var(--border);
  padding: 4px;
  vertical-align: top;
  min-width: 110px;
}

.planificacion-grid th {
  background: var(--panel-alt);
  font-weight: 600;
  text-align: left;
}

.planificacion-grid th input {
  width: 100%;
  border: none;
  background: transparent;
  color: var(--text);
  font-weight: 600;
  font-size: 13px;
  font-family: inherit;
  padding: 2px;
}

.planificacion-grid td textarea {
  width: 100%;
  min-height: 40px;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  resize: vertical;
}

.planificacion-grid-remove-col,
.planificacion-grid-add-col {
  appearance: none;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 2px 4px;
}

.planificacion-grid-remove-col:hover,
.planificacion-grid-add-col:hover {
  color: var(--text);
}

/* Calendario de competición (nuevo módulo, 25/08/2026): la tabla de partidos
   reutiliza .planificacion-grid pero con <input>/<select> en vez de
   <textarea> en las celdas, así que necesita su propia regla de estilo para
   esos controles (mismo aspecto "sin bordes" que el resto de la tabla). */
.calendario-partidos-table td input,
.calendario-partidos-table td select {
  width: 100%;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  padding: 2px;
}

.calendario-ics-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-top: 12px;
}

.calendario-url-input {
  flex: 1;
  min-width: 220px;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel-alt);
  color: var(--muted);
  font-size: 12px;
  font-family: inherit;
}

.planificacion-bloque-card {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}

.planificacion-bloque-fields {
  display: flex;
  gap: 8px;
  align-items: center;
}

.planificacion-bloque-titulo {
  flex: 1;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
}

.planificacion-bloque-texto {
  width: 100%;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 13px;
  font-family: inherit;
  resize: vertical;
}
