:root {
  --bg: #f7f7f9;
  --card: #ffffff;
  --text: #1a1a2e;
  --muted: #6b7280;
  --border: #e5e7eb;
  --accent: #4f46e5;
  --accent-soft: #eef2ff;
  --accent-hover: #4338ca;
  --danger: #dc2626;
  --danger-soft: #fef2f2;
  --success: #16a34a;
  --success-soft: #f0fdf4;
  --oro: #d4a017;
  --plata: #9ca3af;
  --bronce: #cd7f32;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

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

/* El reset genérico de `button` de más abajo pone display:inline-flex en
   TODO <button> — sin !important acá, el atributo/propiedad `hidden` deja
   de esconder cualquier botón al que se le togglee por JS (se queda
   con display:flex igual). Mismo problema le puede pasar a cualquier
   otro elemento con una regla propia de `display`, así que el fix va acá
   una sola vez para todo el sitio en vez de parchar caso por caso. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  padding-bottom: 3rem;
  -webkit-font-smoothing: antialiased;
}

/* Íconos Lucide: [data-lucide] cubre el instante antes de renderizar y
   .lucide el <svg> ya resuelto — sin esto el ícono cae al tamaño nativo
   (grande, sin relación con el texto) apenas createIcons() lo reemplaza. */
[data-lucide], svg.lucide {
  width: 1em;
  height: 1em;
  stroke-width: 2.25;
  vertical-align: -0.15em;
  flex-shrink: 0;
}

/* ---------------------------------------------------------------------
   Header
   ------------------------------------------------------------------- */

header.topbar {
  background: var(--card);
  border-bottom: 1px solid var(--border);
  padding: 0.75rem 1rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  position: sticky;
  top: 0;
  z-index: 20;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  text-decoration: none;
  color: var(--text);
  min-width: 0;
}

.brand-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 9px;
  background: linear-gradient(135deg, var(--accent), #7c73f0);
  color: white;
  flex-shrink: 0;
}

.brand-mark svg.lucide { width: 1.1rem; height: 1.1rem; stroke-width: 2.5; }

.brand-mark img { width: 100%; height: 100%; object-fit: cover; border-radius: 9px; }
.brand-mark:has(img) { background: none; }

/* Selector de imagen del evento: miniatura + botón de archivo con
   estilo propio (el input real queda oculto, el label hace de botón). */
.imagen-picker { display: flex; align-items: center; gap: 0.75rem; }

.imagen-picker-preview {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 9px;
  background: var(--bg);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
  color: var(--muted);
}

.imagen-picker-preview img { width: 100%; height: 100%; object-fit: cover; }

button.btn-archivo {
  min-height: 2.75rem;
  padding: 0 0.8rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
}

button.btn-archivo:hover { background: var(--bg); border-color: var(--accent); color: var(--accent); }

header.topbar h1 {
  font-size: 1rem;
  margin: 0;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

header.topbar nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-shrink: 0;
}

header.topbar nav a,
header.topbar nav button.link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 500;
  background: none;
  border: none;
  padding: 0.3rem;
  cursor: pointer;
  white-space: nowrap;
}

header.topbar nav a:hover,
header.topbar nav button.link:hover { color: var(--accent); }

/* En pantallas muy angostas el texto de los links de nav estorba más de lo
   que ayuda — se dejan solo los íconos (con accesskey via aria-label). */
@media (max-width: 420px) {
  header.topbar nav a span.nav-label,
  header.topbar nav button.link span.nav-label { display: none; }
  header.topbar nav { gap: 0.85rem; }
}

/* ---------------------------------------------------------------------
   Layout base
   ------------------------------------------------------------------- */

main {
  max-width: 720px;
  margin: 0 auto;
  padding: 1rem;
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 1.1rem;
  margin-bottom: 1rem;
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
}

.card h2 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1rem;
  margin: 0 0 0.85rem;
}

.card h2 svg.lucide { color: var(--accent); width: 1.1em; height: 1.1em; }

@media (min-width: 640px) {
  main { padding: 1.5rem; }
  .card { padding: 1.35rem; }
}

/* ---------------------------------------------------------------------
   Formularios
   ------------------------------------------------------------------- */

.filters {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  /* start, no end: si un campo crece (ej. un error de validación debajo),
     no debe arrastrar verticalmente a los demás campos de la fila. */
  align-items: start;
}

@media (min-width: 480px) {
  .filters { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
}

/* Para el caso puntual de un botón suelto (sin label) que acompaña a
   selects/inputs con label en la misma fila de filtros — ese sí debe
   alinearse con la base de los campos, no con el techo de la fila. */
.filtro-boton { align-self: end; }

/* Fila con una cantidad variable de campos (algunos se esconden según la
   configuración del evento) seguida de un botón — con el grid auto-fit de
   .filters el botón se movería de columna cada vez que un campo aparece o
   desaparece. En flex, en cambio, el botón queda fijo pegado a la derecha
   (margin-left:auto) sin importar cuántos campos haya antes. Solo aplica
   desde el mismo ancho en que .filters deja de apilar en una columna. */
@media (min-width: 480px) {
  .fila-nivel { display: flex; flex-wrap: wrap; align-items: end; }
  .fila-nivel > * { flex: 1 1 140px; }
  .fila-nivel > .filtro-boton { flex: 0 0 auto; margin-left: auto; }
}

.url-preview { margin-top: 0.75rem; }

.url-preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.url-preview-label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.url-preview-acciones { display: flex; gap: 0.15rem; flex-shrink: 0; }

.url-preview-value {
  display: block;
  margin-top: 0.25rem;
  color: var(--text);
  font-weight: 600;
  word-break: break-all;
}

/* Caja de fecha con estilo propio: el <input type="date"> nativo se ve
   distinto de alto entre navegadores (sobre todo iOS), así que queda
   invisible tapando toda la caja — el click en cualquier parte abre el
   picker nativo igual, pero el look siempre es el mismo. */
/* Mismo padding/borde/font-size que un input de texto normal (ver regla
   compartida más abajo) para que la altura salga idéntica sin adivinar un
   valor fijo en px. */
.fecha-picker {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
  color: var(--muted);
  font-size: 1rem;
}

.fecha-picker:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

.fecha-picker-texto { color: var(--text); font-size: 1rem; }
.fecha-picker-texto.muted { color: var(--muted); font-size: 1rem; }

input[type="date"].fecha-picker-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  opacity: 0;
  cursor: pointer;
}

/* El login es un formulario secuencial (email -> contraseña), no un grupo
   de filtros independientes: siempre 1 columna, sin importar el ancho. */
.gate .filters { grid-template-columns: 1fr; }

label {
  display: flex;
  align-items: center;
  font-size: 0.8rem;
  color: var(--muted);
  margin-bottom: 0.3rem;
}

select, input[type="text"], input[type="number"], input[type="date"], input[type="email"], input[type="password"] {
  width: 100%;
  /* font-size 1rem (16px) a propósito: menos y Safari en iOS hace zoom
     automático al enfocar el campo. */
  padding: 0.6rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 1rem;
  background: var(--card);
  color: var(--text);
}

select:focus, input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

input[readonly] {
  background: var(--bg);
  color: var(--muted);
}

input.campo-invalido, select.campo-invalido { border-color: var(--danger); }
input.campo-invalido:focus, select.campo-invalido:focus { box-shadow: 0 0 0 3px var(--danger-soft); }

.field-error {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  color: var(--danger);
  font-size: 0.78rem;
  margin: 0.35rem 0 0;
}

button.lapiz {
  background: none;
  border: none;
  padding: 0 0.2rem;
  min-height: 0;
  margin-left: 0.3rem;
  color: var(--muted);
  cursor: pointer;
  font-size: 1em;
  line-height: 1;
}

button.lapiz:hover { background: none; color: var(--accent); }

button {
  cursor: pointer;
  border: none;
  border-radius: 8px;
  padding: 0.7rem 1.1rem;
  font-size: 0.92rem;
  background: var(--accent);
  color: white;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  min-height: 2.75rem;
  transition: background 0.15s;
}

button:hover { background: var(--accent-hover); }
button.secondary { background: var(--bg); color: var(--text); border: 1px solid var(--border); }
button.secondary:hover { background: var(--border); }
button.danger { background: var(--danger-soft); color: var(--danger); }
button.danger:hover { background: #fee2e2; }
button:disabled { opacity: 0.5; cursor: not-allowed; }

.spin { animation: girar 0.8s linear infinite; }
@keyframes girar { to { transform: rotate(360deg); } }

button.pequeno { min-height: 2.25rem; padding: 0.45rem 0.8rem; font-size: 0.85rem; }

.paginacion { display: flex; align-items: center; justify-content: center; gap: 0.75rem; margin-top: 0.75rem; }

/* Botón con título + aclaración en una segunda línea más chica (ej.
   "Finalizar evento" / "(Esto genera resultados finales)"). */
button.btn-dos-lineas { padding: 0.6rem 1.1rem; text-align: left; }
.btn-lineas { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; line-height: 1.3; }
.btn-linea-2 { font-size: 0.75rem; font-weight: 500; opacity: 0.85; }

/* ---------------------------------------------------------------------
   Listas de datos (reemplazan <table> — nada de scroll horizontal en
   celular, que es donde vive prácticamente todo el tráfico de esta app)
   ------------------------------------------------------------------- */

.data-list { display: flex; flex-direction: column; gap: 0.6rem; }

.data-row {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.75rem 0.9rem;
}

.data-row-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.data-row-main-secundaria { margin-top: 0.5rem; }

.flex-fijo { flex-shrink: 0; }

.dns-records { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.75rem; }
.dns-record code { overflow-wrap: anywhere; }

.data-row-title {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  font-size: 0.95rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.data-row-meta { color: var(--muted); font-size: 0.82rem; margin-top: 0.3rem; }

.data-row-main a {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.85rem;
  flex-shrink: 0;
  color: var(--accent);
  text-decoration: none;
  font-weight: 500;
}

.data-row-main a:hover { text-decoration: underline; }

/* Fila de gimnasta: mismo .data-row y mismo dato, dos variantes de layout
   que se togglean por media query — nunca un <table> (ver comentario más
   arriba: acá eso significa reintroducir scroll horizontal en mobile,
   que es donde vive casi todo el tráfico de esta app). En mobile es una
   card condensada de una sola línea de meta; desde el ancho en que entra
   cómodo, se ve como tabla vía grid, con un encabezado de columnas una
   sola vez arriba de la lista en vez de repetir NIVEL/FRANJA/SUBDIVISIÓN
   en cada card. */
.gimnasta-row-desktop { display: none; }

.gimnasta-nombre-club { display: flex; flex-direction: column; min-width: 0; }

.gimnasta-header {
  display: none;
  padding: 0 0.9rem;
  margin-bottom: 0.4rem;
  color: var(--muted);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.info-btn.borrar { color: var(--danger); }
.info-btn.borrar:hover { background: var(--danger-soft); color: var(--danger); border-radius: 8px; }

@media (min-width: 700px) {
  .gimnasta-row-mobile { display: none; }
  .gimnasta-row-desktop,
  .gimnasta-header {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr 1fr 2.25rem;
    gap: 0.75rem;
    align-items: center;
  }
}

/* Envuelve las flechas + la card de datos: las flechas quedan fuera de la
   card (control de posición), no adentro (contenido). */
.reordenable-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.reordenable-item .data-row { flex: 1; min-width: 0; }

/* Flechas de reordenar (niveles/franjas): la posición en la lista ES el
   orden, así que esto reemplaza un input numérico donde dos filas podían
   terminar peleando por el mismo valor. */
.reorder-btns {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  flex-shrink: 0;
}

.reorder-btns button {
  min-height: 0;
  width: 1.75rem;
  height: 1.4rem;
  padding: 0;
  background: var(--bg);
  color: var(--muted);
  border: 1px solid var(--border);
}

.reorder-btns button:hover { background: var(--border); }
.reorder-btns button:disabled { opacity: 0.3; cursor: not-allowed; }

/* Menú de opciones "3 puntitos" (ej. Configurar/Duplicar de un evento en el
   panel) — el trigger reusa .info-btn, el popover se ancla a la derecha del
   botón para no salirse de la card en mobile. */
.menu-kebab { position: relative; flex-shrink: 0; }

.menu-dropdown {
  position: absolute;
  top: calc(100% + 0.3rem);
  right: 0;
  z-index: 30;
  min-width: 160px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
  padding: 0.35rem;
  display: flex;
  flex-direction: column;
}

.menu-dropdown a,
.menu-dropdown button {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  min-height: 0;
  padding: 0.55rem 0.6rem;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--text);
  font-size: 0.88rem;
  font-weight: 500;
  text-align: left;
  text-decoration: none;
}

.menu-dropdown a:hover,
.menu-dropdown button:hover { background: var(--accent-soft); color: var(--accent); }
.menu-dropdown svg.lucide { color: var(--muted); flex-shrink: 0; }
.menu-dropdown a:hover svg.lucide,
.menu-dropdown button:hover svg.lucide { color: var(--accent); }

/* Modal de edición (niveles/franjas) */
dialog {
  border: none;
  border-radius: 14px;
  padding: 0;
  width: min(420px, 92vw);
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
}

dialog::backdrop { background: rgba(15, 15, 30, 0.45); }

.modal-body { padding: 1.25rem; }

/* X de cierre, esquina superior derecha de todo <dialog> — el propio
   <dialog> ya queda posicionado (absolute/fixed) por el user agent al
   mostrarse con .show()/.showModal(), así que basta con position:absolute
   acá para anclarla a esa caja sin tocar el "position" del dialog (eso
   rompería el centrado nativo). */
.modal-cerrar {
  position: absolute;
  top: 0.65rem;
  right: 0.65rem;
  z-index: 1;
}

/* Los modales de filtros no tienen encabezado propio (h2): el grid de
   campos arranca pegado arriba y quedaría debajo de la X. */
dialog.card > .filters { padding-top: 1.75rem; }

/* Filtros de resultados: el mismo <dialog> se ve inline como cualquier
   .card en desktop (mostrado vía .show(), no .showModal() — sin backdrop,
   sin ancho de modal) y como modal de verdad recién desde donde .filters
   apila en una sola columna. */
#btn-abrir-filtros { position: relative; }

.badge-punto {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent);
}

/* Campo con un botón pegado al costado del select (ej. limpiar filtros
   junto a Subdivisión en desktop) — mismo alto que el select, sin
   forzarlo a su propia columna del grid de .filters. */
.campo-con-boton { display: flex; align-items: center; gap: 0.4rem; }
.campo-con-boton select { flex: 1; min-width: 0; }

/* "Limpiar filtros" en desktop es solo el ícono junto al último select;
   en mobile es texto + ícono al lado de "Cerrar" — nunca los dos a la
   vez, por eso se togglean al revés uno del otro en el mismo breakpoint. */
#btn-limpiar-filtros-desktop { display: none; }

@media (min-width: 640px) {
  #btn-abrir-filtros { display: none; }
  /* dialog trae position:absolute del stylesheet nativo del navegador aun
     mostrado con .show() (no solo con .showModal()) — sin esto no respeta
     el ancho del contenedor ni empuja el resto del contenido hacia abajo,
     queda flotando encima como si fuera un modal de verdad. */
  #modal-filtros { position: static; width: auto; }
  /* "Aplicar"/X solo tienen sentido cuando los filtros son un modal de
     verdad (mobile) — acá no hay nada que cerrar. */
  #wrap-cerrar-filtros { display: none; }
  #modal-filtros .modal-cerrar { display: none; }
  #btn-limpiar-filtros-desktop { display: inline-flex; }
}

/* Modal de orden — a diferencia de los filtros, siempre es modal de
   verdad (nunca inline): es una sola acción por click, no un formulario
   de varios campos que valga la pena tener siempre a la vista. */
.orden-lista { display: flex; flex-direction: column; }

/* Reset del <button> genérico (fondo accent, radio, negrita) — acá es una
   fila de lista clickeable completa, no un botón de acción. */
.orden-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 0.6rem 0;
  min-height: 0;
  border-radius: 0;
  background: none;
  color: var(--text);
  font-size: 1rem;
  font-weight: 400;
  text-align: left;
}

.orden-fila:hover { background: var(--accent-soft); }
.orden-fila:not(:last-child) { border-bottom: 1px solid var(--border); }
.orden-fila svg.lucide { color: var(--muted); flex-shrink: 0; }

.modal-body h2 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1rem;
  font-size: 1.05rem;
}

.modal-body h2 svg.lucide { color: var(--accent); width: 1.1em; height: 1.1em; }

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.9rem;
}

.checkbox-label {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
  font-size: 0.9rem;
  color: var(--text);
  cursor: pointer;
}

.checkbox-label input[type="checkbox"] {
  width: 1.15rem;
  height: 1.15rem;
  accent-color: var(--accent);
  cursor: pointer;
}

.info-btn {
  background: none;
  border: none;
  padding: 0.2rem;
  min-height: 0;
  color: var(--muted);
  cursor: pointer;
}

.info-btn:hover { background: none; color: var(--accent); }

.tooltip-box {
  margin-top: 0.6rem;
  padding: 0.75rem 0.85rem;
  background: var(--accent-soft);
  border-radius: 8px;
  font-size: 0.82rem;
  line-height: 1.5;
}

/* min-height/background/color repetidos explícito acá porque .chip-link
   se usa tanto en <a> como en <button> — sin esto, el reset genérico de
   `button` (fondo violeta sólido, sin min-height propio) se cuela cuando
   la clase cae en un <button>, y el de <a> queda con otro alto. */
.chip-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  padding: 0.6rem 1rem;
  min-height: 2.75rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  text-decoration: none;
  font-size: 0.88rem;
  font-weight: 500;
}

.chip-link:hover { background: var(--bg); border-color: var(--accent); color: var(--accent); }

/* Link inline dentro de un párrafo (no una caja como .chip-link) — para
   frases del tipo "revisa la configuración del evento" donde la palabra
   ya está en el texto y solo hace falta activarla, con una flechita de
   "ir a" al final. */
/* background/border/padding/min-height explícitos porque, además de en
   <a>, esta clase también se usa en <button> — sin esto el reset
   genérico de `button` (fondo violeta sólido) se cuela, mismo problema
   que ya tuvimos con .chip-link. */
.link-inline {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  background: none;
  border: none;
  padding: 0;
  min-height: 0;
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  cursor: pointer;
}
.link-inline:hover { background: none; text-decoration: underline; }
.link-inline svg.lucide { width: 0.85em; height: 0.85em; }

.error-list {
  margin: 0.75rem 0;
  padding: 0.75rem 1rem 0.75rem 1.75rem;
  background: var(--danger-soft);
  border-radius: 8px;
  color: var(--danger);
  font-size: 0.85rem;
  line-height: 1.6;
}

/* Ícono destacado en violeta dentro de un botón/chip cuyo texto queda en
   el color normal — un solo lugar para este color, se aplica agregando
   esta clase al <i data-lucide="..."> (Lucide preserva las clases
   propias del elemento original al reemplazarlo por el <svg>). */
.icono-acento { color: var(--accent); }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

.pill-success { background: var(--success-soft); color: var(--success); }
.pill-danger { background: var(--danger-soft); color: var(--danger); }

/* Franjas de un nivel: chips "F1"/"F2"/... + botón de agregar. Solo el
   último chip lleva botón de borrar (la numeración es un stack: se agrega
   y se quita siempre desde el final, nunca huecos ni nombres a mano). */
.franja-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.3rem;
}

.franja-chip button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  min-height: 0;
  margin-left: 0.15rem;
  color: inherit;
  cursor: pointer;
}

.franja-chip button:hover { background: none; }

.franja-chip button svg.lucide { width: 0.85em; height: 0.85em; }

.franja-add {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  background: none;
  border: 1px dashed var(--border);
  color: var(--muted);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
  min-height: 0;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
}

.franja-add:hover { background: none; border-color: var(--accent); color: var(--accent); }
.franja-add svg.lucide { width: 0.85em; height: 0.85em; }

.muted { color: var(--muted); font-size: 0.85rem; }

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 1.5rem 1rem;
  color: var(--muted);
  text-align: center;
}

.empty-state svg.lucide { width: 1.6rem; height: 1.6rem; opacity: 0.6; }

/* Cuando el empty-state es toda la pantalla (evento no encontrado, sin
   eventos, link roto) en vez de un aviso adentro de una lista/card. */
main.pantalla-vacia { display: flex; align-items: center; justify-content: center; min-height: 60vh; }

/* ---------------------------------------------------------------------
   Ranking de resultados (index.html)
   ------------------------------------------------------------------- */

.rank-list { display: flex; flex-direction: column; gap: 0.6rem; }

/* En mobile, .rank-card ya trae su propio borde/esquinas — envolverlas en
   otro .card (fondo blanco + padding propio) es una segunda capa que no
   suma nada visual y solo le resta ancho de texto a cada fila (justo lo
   que hacía que los nombres se cortaran). El título y el pie quedan
   directo sobre el fondo gris de la página; en desktop, donde no falta
   ancho, se deja la tarjeta como el resto de la página. */
@media (max-width: 639.98px) {
  .card-lista {
    background: none;
    border: none;
    box-shadow: none;
    padding: 0;
  }
}

.rank-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.6rem 0.85rem;
}

.rank-card-head { display: flex; align-items: center; gap: 0.7rem; }

.rank-card-info { flex: 1; min-width: 0; }
.rank-card-info strong { display: block; font-size: 0.95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.rank-card-total {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--accent);
  flex-shrink: 0;
}

/* Medalla por posición real (1°/2°/3°/resto premiado) — no todo lo
   premiado es "igual": oro/plata/bronce son la convención universal, y el
   morado del theme para 4° en adelante deja claro que sigue premiada sin
   inventar un color ad-hoc nuevo. Misma lógica para el ícono del All
   Around (.rank-card-total) y el de cada aparato (.score-cell .value). */
.medalla-oro { color: var(--oro); }
.medalla-plata { color: var(--plata); }
.medalla-bronce { color: var(--bronce); }
.medalla-otra { color: var(--accent); }

/* Estrella de favorito — mismo dorado que la medalla de 1er lugar cuando
   está activa; el ícono es el mismo lucide "star" en los dos estados, se
   rellena vía fill en vez de usar un ícono distinto (lucide no trae una
   variante "star" ya rellena). */
.btn-favorito.activo { color: var(--oro); }
.btn-favorito.activo svg.lucide { fill: var(--oro); }

.rank-card-scores {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

.score-cell { text-align: center; }
.score-cell .label { display: block; font-size: 0.65rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; }
.score-cell .value {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  font-size: 0.9rem;
  font-weight: 600;
  margin-top: 0.15rem;
}

/* ---------------------------------------------------------------------
   Ingreso de puntajes: selector de aparato, tarjeta por gimnasta + switch
   táctil
   ------------------------------------------------------------------- */

/* Selector de aparato: reemplaza el <select> por un ícono por aparato —
   el activo se pinta con el color del theme, el resto en gris, como
   cualquier control segmentado. */
.aparato-selector {
  display: flex;
  gap: 0.6rem;
  margin-bottom: 1rem;
}

.aparato-btn {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  min-height: 0;
  padding: 0.75rem 0.5rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
}

.aparato-btn:hover { background: var(--accent-soft); }
.aparato-btn svg { width: 26px; height: 26px; }

/* Tick de "aparato completo" — a propósito ignora el filtro activo (ver
   actualizarCompletitudAparatos en puntajes.html), así que no cambia de
   opinión según qué filtro tenga puesto el juez en este momento. */
.aparato-completo {
  position: absolute;
  top: 0.3rem;
  right: 0.3rem;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  background: var(--success);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}

.aparato-completo svg.lucide { width: 0.65rem; height: 0.65rem; stroke-width: 3; }

.aparato-btn.activo {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}

.entry-list { display: flex; flex-direction: column; gap: 0.6rem; }

.entry-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.85rem 0.9rem;
}

/* Marcada al intentar guardar sin tocar el puntaje — no bloquea el
   guardado (las demás filas se guardan igual), es solo un aviso visual
   que desaparece apenas el juez tipea un dígito o marca "no compite". */
.entry-card.incompleto { border-color: var(--danger); }

.entry-card-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.entry-card-info { flex: 1; min-width: 0; }
.entry-card-info strong { display: block; font-size: 0.95rem; }
.entry-card-info span { display: block; color: var(--muted); font-size: 0.78rem; margin-top: 0.1rem; }

.entry-card-head .in-puntaje { flex: 0 0 auto; width: 5.5rem; font-size: 1.05rem; text-align: center; }

/* Toggle de "no compite": mismo look que cualquier .info-btn, coloreado en
   --danger cuando está activo (la gimnasta queda excluida de este
   aparato) — mismo patrón que .btn-favorito.activo. */
.btn-no-compite.activo { color: var(--danger); }

/* Puntaje sin tocar (el "0.000" de arranque de la máscara) se ve apagado
   — en cuanto el juez tipea el primer dígito pasa a color de texto normal
   (ver clase .tocado que agrega app.js). */
.in-puntaje:not(.tocado) { color: var(--muted); }

.switch { display: flex; align-items: center; gap: 0.45rem; cursor: pointer; user-select: none; flex-shrink: 0; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch-track {
  position: relative;
  width: 2.5rem;
  height: 1.5rem;
  background: var(--border);
  border-radius: 999px;
  transition: background 0.15s;
  flex-shrink: 0;
}

.switch-track::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 1.25rem;
  height: 1.25rem;
  background: white;
  border-radius: 50%;
  transition: transform 0.15s;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

.switch input:checked + .switch-track { background: var(--danger); }
.switch input:checked + .switch-track::before { transform: translateX(1rem); }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 3px var(--accent-soft); }

.switch-label { font-size: 0.78rem; color: var(--muted); line-height: 1.15; max-width: 4rem; }

/* ---------------------------------------------------------------------
   Auth gate
   ------------------------------------------------------------------- */

.gate {
  max-width: 380px;
  margin: 2rem auto;
  text-align: center;
}

.gate-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  margin: 0 auto 1rem;
}

.gate-icon svg.lucide { width: 1.4rem; height: 1.4rem; }

/* Solo el input de código de acceso (carga/ingreso) va centrado y espaciado
   como un código — el de email/password en login.html debe verse normal. */
.gate input#gate-input { text-align: center; font-size: 1.3rem; letter-spacing: 0.15em; }

/* Mobile-first: una opción por línea (ancho completo) — a partir de
   480px pasan a compartir fila. */
.row { display: flex; flex-direction: column; gap: 0.5rem; }
.row > * { flex: 1; }

@media (min-width: 480px) {
  .row { flex-direction: row; flex-wrap: wrap; }
}

.toast {
  position: fixed;
  bottom: 1.25rem;
  left: 50%;
  transform: translateX(-50%);
  max-width: calc(100vw - 2rem);
  background: var(--text);
  color: white;
  padding: 0.65rem 1.15rem;
  border-radius: 999px;
  font-size: 0.88rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  opacity: 0;
  transition: opacity 0.2s;
  pointer-events: none;
  z-index: 50;
}

.toast.show { opacity: 1; }
.toast.error { background: var(--danger); }
.toast.ok { background: var(--success); }
