/* =====================================================================
   BCN Surf School · componentes.css
   Fuente única de los componentes del sistema «Menos es más». Norma: DESIGN.md.
   Copiado literal de la CAPA 2 de .claude/banco/propuestas/minimal/minimal.css
   (23-sep-2026, fundación F3). Desde aquí, todo cambio de componente se hace en
   ESTE fichero y se documenta en DESIGN.md › Components; minimal.css queda
   congelado como referencia de lo aprobado.
   Necesita delante /fonts/marca.css y /estilos-base.css (tokens CAPA 1 y modo
   ligero CAPA 1b). Solo lo enlazan las páginas migradas:
   <body class="app v2 [con-barra|con-accion]">.
   Sección 20 (al final): piezas compartidas de F3b (DESIGN.md › Piezas compartidas).
   ===================================================================== */


/* =====================================================================
   CAPA 2 · COMPONENTES → componentes.css
   Solo lo enlazan las páginas migradas: <body class="app v2 [con-barra|con-accion]">.
   ===================================================================== */

/* ---------- 1. Base de la página migrada ---------- */
.v2 {
  font-family: var(--f-cuerpo); font-size: var(--fs-17); font-weight: var(--p-texto); line-height: var(--lh-ui);
  color: var(--c-texto); background: var(--c-fondo);
  -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent;
  font-variant-numeric: tabular-nums; accent-color: var(--c-turquesa-h); caret-color: var(--c-turquesa-h); /* como en la web */
}
/* Gana a body.app de estilos-base (degradados de fondo) */
body.app.v2 { margin: 0; min-height: 100vh; min-height: 100dvh; background: var(--c-fondo); color: var(--c-texto); font-family: var(--f-cuerpo); }
.v2.con-barra { --alto-barra: var(--barra-h); }
.v2.con-accion { --alto-accion: var(--accion-h); }
/* Reinicios con :where(): especificidad (0,0,1), nunca ganan a una clase .ui-* */
:where(.v2), :where(.v2) *, :where(.v2) *::before, :where(.v2) *::after { box-sizing: border-box; }
:where(.v2) a { color: inherit; text-decoration: none; }
:where(.v2) button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
:where(.v2) :is(h1, h2, h3, h4, p, ul, ol, figure) { margin: 0; }
:where(.v2) :is(h1, h2, h3, h4) { font-family: inherit; font-size: inherit; font-weight: var(--p-fuerte); letter-spacing: normal; color: inherit; text-wrap: balance; }
:where(.v2) :is(ul, ol) { padding: 0; list-style: none; }
.v2 ::selection { background: var(--c-turquesa-halo); color: var(--c-tinta); }   /* 13,5:1 */
:where(.v2) :focus:not(:focus-visible) { outline: none; }
:where(.v2) :focus-visible { outline: 2px solid var(--c-foco); outline-offset: 2px; }
.ui-ico { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.75;
  stroke-linecap: round; stroke-linejoin: round; vertical-align: middle; }
.ui-ico--16 { width: 16px; height: 16px; }
.ui-ico--24 { width: 24px; height: 24px; }
.ui-marina { color: var(--c-marina); }
.ui-num { font-variant-numeric: tabular-nums; }
.ui-solo-lector { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Contenedor de pantalla: margen lateral, zona segura arriba y hueco para lo fijo de abajo */
.ui-contenido { width: 100%; max-width: 640px; margin: 0 auto;
  padding: calc(var(--zona-arriba) + 8px) var(--margen) calc(var(--alto-barra) + var(--alto-accion) + var(--zona-abajo) + 24px); }

/* ---------- 4. Cabecera ----------
   Raíz (.ui-cab): fecha + saludo + dos iconos. Sección (.ui-cab--seccion): título + UNA acción
   (filtro «Todas ▾» o un icono). Ninguna de las dos es fija ni lleva volver.
   Paso (.ui-cab-paso): volver + título corto centrado (o vacío si debajo va un título grande);
   se queda pegada arriba al hacer scroll, opaca, sin desenfoque. */
.ui-cab { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; padding-top: 8px; }
.ui-cab--seccion { align-items: center; }
.ui-cab-txt { min-width: 0; }
.ui-cab-fecha { font-size: var(--fs-15); color: var(--c-texto-2); }
.ui-cab-titulo { font: var(--p-display) var(--fs-titulo)/var(--lh-titulo) var(--f-display); letter-spacing: -.005em; word-spacing: var(--ws-display);
  color: var(--c-tinta); margin-top: 2px; text-wrap: balance; overflow-wrap: anywhere; }
.ui-cab-acciones { display: flex; gap: 2px; margin-right: -10px; flex: none; }
.ui-cab-paso { position: sticky; top: 0; z-index: var(--z-cabecera); display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center; min-height: 44px; background: var(--c-fondo);
  /* anula el relleno superior de .ui-contenido y lo lleva dentro: al pegarse, tapa la zona del notch */
  margin: calc(-1 * (var(--zona-arriba) + 8px)) -10px 0; padding-top: calc(var(--zona-arriba) + 8px); }
.ui-cab-paso-tit { text-align: center; font-size: var(--fs-17); font-weight: var(--p-fuerte); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-cab-paso-sub { display: block; font-size: var(--fs-13); font-weight: var(--p-texto); color: var(--c-texto-2); }

/* Botón de icono: sin caja, 44 px de toque */
.ui-btn-ico { position: relative; display: inline-grid; place-items: center; width: var(--toque); height: var(--toque); flex: none;
  border-radius: var(--r-redondo); color: var(--c-marina); transition: background-color var(--dur-estado) var(--ease-out); }
.ui-btn-ico .ui-ico { width: 24px; height: 24px; }
.ui-btn-ico:active { background: var(--c-marina-suave); }
.ui-insignia { position: absolute; top: 9px; right: 10px; width: 9px; height: 9px; border-radius: var(--r-redondo);
  background: var(--c-marina); box-shadow: 0 0 0 2px var(--c-fondo); }

/* Título de bloque: frase normal, nada de antetítulos en mayúsculas */
.ui-bloque { margin-top: var(--bloque); }
.ui-bloque-tit { display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: var(--fs-17); font-weight: var(--p-fuerte); margin-bottom: 10px; }

/* ---------- 5. Tarjeta de próxima clase (la ÚNICA tarjeta del inicio) ---------- */
.ui-tarjeta { background: var(--c-superficie); border-radius: var(--r-control); }
.ui-proxima { position: relative; display: block; padding: 16px 16px 14px; border-radius: var(--r-control) var(--r-control) 0 0; }
/* El único dibujo del inicio: el pictograma de la actividad, en línea marina y sin tesela */
.ui-proxima-picto { position: absolute; top: 16px; right: 14px; width: 30px; height: 30px; fill: none; stroke: var(--c-marina);
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.ui-proxima > .ui-proxima-act, .ui-proxima > .ui-proxima-cuando { padding-right: 40px; }
.ui-proxima:active { background: var(--c-marina-suave); }
.ui-proxima-act { font-size: var(--fs-20); font-weight: var(--p-fuerte); line-height: 1.2; }
.ui-proxima-cuando { margin-top: 4px; font-size: var(--fs-17); color: var(--c-texto-2); }
.ui-proxima-cuando .ui-hora { font-size: inherit; }
.ui-proxima-olas { margin-top: 10px; }
.ui-proxima-pie { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 52px;
  margin: 0 16px; border-top: 1px solid var(--c-regla); }
.ui-proxima-sitio { display: flex; align-items: center; gap: 8px; min-width: 0; color: var(--c-texto); }
.ui-proxima-sitio .ui-ico { color: var(--c-marina); }

/* Hora: Satoshi en marina, cifras tabulares, tamaño contenido. Nunca Clash negra */
.ui-hora { font-family: var(--f-cuerpo); font-weight: var(--p-fuerte); color: var(--c-marina);
  font-variant-numeric: tabular-nums; letter-spacing: -.005em; }

/* ---------- 6. Botones ---------- */
.ui-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: var(--control);
  padding: 0 20px; border-radius: var(--r-control); font-size: var(--fs-17); font-weight: var(--p-fuerte); line-height: 1.2;
  text-align: center; transition: background-color var(--dur-estado) var(--ease-out), transform var(--dur-estado) var(--ease-out); }
.ui-btn:active { transform: scale(.98); }
.ui-btn--bloque { display: flex; width: 100%; }
.ui-btn--principal { background: var(--c-accion); color: var(--c-accion-txt); }
.ui-btn--principal:active { background: var(--c-accion-pulsada); }
/* Alegre: el principal en turquesa con texto tinta (10,4:1; pulsado 9,1:1), como el «Pagar» de la web.
   SOLO para cerrar una compra o una reserva del alumno («Pagar» o «Reservar» del paso final, «Comprar
   bono», «Comprar pack», «Continuar al pago», «Canjear»…) y el «Reservar clase» del inicio del alumno.
   Uno por pantalla como mucho y nunca en una pantalla de trabajo. Solo cambia los tokens: el
   deshabilitado (arena 2 + tinta 2) y el ocupado salen de sus reglas de siempre.
   <button class="ui-btn ui-btn--principal ui-btn--alegre ui-btn--bloque">Pagar</button> */
.ui-btn--alegre { --c-accion: var(--c-turquesa); --c-accion-pulsada: var(--c-turquesa-pulsado); --c-accion-txt: var(--c-tinta); }
.ui-btn--secundario { background: var(--c-marina-suave); color: var(--c-marina); }
.ui-btn--secundario:active { background: var(--c-marina-medio); }
.ui-btn[disabled], .ui-btn[aria-disabled="true"] { background: var(--c-arena-2); color: var(--c-tinta-2); transform: none; cursor: default; }
@media (hover: hover) and (pointer: fine) {
  .ui-btn--principal:hover { background: var(--c-accion-pulsada); }
  .ui-btn--secundario:hover { background: var(--c-marina-medio); }
}
/* Enlace: marina, sin subrayado hasta el foco; 44 px de toque aunque se vea pequeño */
.ui-enlace { display: inline-flex; align-items: center; gap: 2px; min-height: var(--toque); color: var(--c-marina);
  font-weight: var(--p-texto); }
.ui-enlace .ui-ico { width: 16px; height: 16px; }
.ui-enlace:focus-visible { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* «Todas ▾»: el filtro de actividad sale de la pantalla y vive en una hoja */
.ui-filtro { display: inline-flex; align-items: center; min-height: var(--toque); flex: none; }
.ui-filtro-in { display: inline-flex; align-items: center; gap: 4px; height: 34px; padding: 0 10px 0 14px; border-radius: var(--r-pastilla);
  background: var(--c-marina-suave); color: var(--c-marina); font-size: var(--fs-15); font-weight: var(--p-texto);
  transition: background-color var(--dur-estado) var(--ease-out); }
.ui-filtro-in .ui-ico { width: 16px; height: 16px; stroke-width: 2; }
.ui-filtro:active .ui-filtro-in { background: var(--c-marina-medio); }

/* ---------- 7. Tira de días a lo Apple ---------- */
.ui-dias { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); margin: 16px -6px 0; padding-bottom: 6px; border-bottom: 1px solid var(--c-regla); }
.ui-dia { display: flex; flex-direction: column; align-items: center; gap: 4px; min-height: 64px; padding: 2px 0 4px; }
.ui-dia-sem { font-size: var(--fs-12); font-weight: var(--p-texto); color: var(--c-texto-2); line-height: 16px; }
.ui-dia-num { display: grid; place-items: center; width: 36px; height: 36px; border-radius: var(--r-redondo);
  font-size: var(--fs-17); font-weight: var(--p-texto); color: var(--c-tinta);
  transition: background-color var(--dur-estado) var(--ease-out), color var(--dur-estado) var(--ease-out); }
.ui-dia-punto { width: 4px; height: 4px; border-radius: var(--r-redondo); visibility: hidden; } /* sin puntos (notas de Nacho, 23-sep): el hueco se queda y la tira no salta */
.ui-dia--hoy .ui-dia-num { color: var(--c-marina); font-weight: var(--p-fuerte); }
/* Día sin clases: letra y número en hormigón (4,3:1 sobre arena, ≥ 3:1) y «sin clases» en su aria-label.
   Sigue siendo tocable. Hoy sin clases: apagado, pero en 700 */
.ui-dia--vacio .ui-dia-num, .ui-dia--vacio .ui-dia-sem { color: var(--c-hormigon); }
/* Día elegido: turquesa con el número en tinta (10,4:1) y un anillo de turquesa hondo (4,9:1 sobre arena):
   el estado se ve también en gris (WCAG 1.4.11). Así «hoy» (número marina) y «elegido» no se confunden */
.ui-dia[aria-pressed="true"] .ui-dia-num { background: var(--c-turquesa); color: var(--c-tinta); font-weight: var(--p-fuerte);
  box-shadow: inset 0 0 0 1.5px var(--c-turquesa-h); }
.ui-dia:active .ui-dia-num { background: var(--c-marina-suave); }
.ui-dia[aria-pressed="true"]:active .ui-dia-num { background: var(--c-turquesa-pulsado); }

/* ---------- 8. Filas ---------- */
/* Encabezado de día dentro de una lista */
.ui-lista-cab { margin: 24px 0 2px; font-size: var(--fs-15); font-weight: var(--p-fuerte); color: var(--c-texto-2); }
.ui-lista-cab:first-child { margin-top: 20px; }

/* Lista de clases de un día: contenedor sin estilo propio (las filas llevan sus reglas) */
.ui-lista { display: block; }
/* Fila de clase: hora · nombre · precio · chevron. Sin caja: solo reglas finas */
.ui-clase { display: grid; grid-template-columns: 52px minmax(0, 1fr) auto 14px; align-items: center; column-gap: 12px;
  min-height: var(--fila); padding: 10px 0; border-bottom: 1px solid var(--c-regla); color: var(--c-texto);
  transition: background-color var(--dur-estado) var(--ease-out); }
.ui-clase:last-child { border-bottom: 0; }
.ui-clase .ui-hora { font-size: var(--fs-17); }
.ui-clase-nombre { font-size: var(--fs-17); font-weight: var(--p-texto); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-clase-nota { display: block; margin-top: 2px; font-size: var(--fs-15); color: var(--c-texto-2); }
.ui-clase-precio { font-size: var(--fs-17); font-weight: var(--p-texto); color: var(--c-texto); }
.ui-clase > .ui-ico { width: 14px; height: 14px; color: var(--c-chevron); stroke-width: 2.25; }
.ui-clase:active { background: var(--c-marina-suave); box-shadow: -8px 0 0 var(--c-marina-suave), 8px 0 0 var(--c-marina-suave); }
/* Ya empezó: atenuada, sin chip, sin precio, no se toca */
.ui-clase--pasada { color: var(--c-texto-2); pointer-events: none; }
.ui-clase--pasada .ui-hora { color: var(--c-texto-2); font-weight: var(--p-texto); }
/* Completa: se lee, pero no invita */
.ui-clase--completa .ui-clase-nombre, .ui-clase--completa .ui-clase-precio { color: var(--c-texto-2); }

/* Etiquetas de excepción: texto, no pastilla. Solo cuando cambian la decisión */
.ui-etq { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-15); font-weight: var(--p-texto); color: var(--c-texto-2); }
.ui-etq--aviso { color: var(--c-aviso); }
.ui-etq--error { color: var(--c-error); }
.ui-etq--bueno { color: var(--c-turquesa-h); }   /* 5,4:1 sobre hueso · 4,9:1 sobre arena */
/* «Olas confirmadas» / «Surf confirmado» / «Finalizada»: punto «luz de mar», centro hondo y halo turquesa de 3 px
   (decorativo). El halo lo distingue de la bolita lisa de Surf #2F9C93. Solo aspecto: qué dato decide la
   etiqueta, dónde sale y qué dice no cambia */
.ui-etq--bueno::before { content: ""; width: 8px; height: 8px; border-radius: var(--r-redondo); background: var(--c-turquesa-h);
  box-shadow: 0 0 0 3px var(--c-turquesa-halo); flex: none; }

/* Grupo (lista agrupada sobre hueso): filas con separador sangrado */
.ui-grupo { background: var(--c-superficie); border-radius: var(--r-control); overflow: hidden; }
/* Lo tuyo: «Tu plan» en Cuenta, en turquesa suave. Uno por pantalla y nunca junto a un botón alegre (por eso
   «Tu próxima clase» del inicio se queda en hueso). Iconos marina (10,4:1) y cifras en tinta 2 (7,3:1): el
   dinero no se colorea */
.ui-grupo--destacado { background: var(--c-turquesa-suave); }
.ui-fila { position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; column-gap: 14px;
  min-height: 52px; padding: 10px 16px; color: var(--c-texto); transition: background-color var(--dur-estado) var(--ease-out); }
.ui-fila + .ui-fila::before { content: ""; position: absolute; top: 0; left: 16px; right: 0; height: 1px; background: var(--c-regla); }
.ui-fila--icono + .ui-fila::before, .ui-fila--icono + .ui-fila--icono::before { left: 54px; }
.ui-fila > .ui-ico { width: 24px; height: 24px; color: var(--c-marina); }
.ui-fila--sin-icono { grid-template-columns: minmax(0, 1fr) auto; }
.ui-fila-tit { display: block; font-size: var(--fs-17); font-weight: var(--p-texto); }
.ui-fila-sub { display: block; margin-top: 1px; font-size: var(--fs-15); color: var(--c-texto-2); line-height: 1.35; }
.ui-fila-valor { display: inline-flex; align-items: center; gap: 10px; font-size: var(--fs-17); color: var(--c-texto-2); white-space: nowrap; }
.ui-fila-valor .ui-chev { width: 14px; height: 14px; color: var(--c-chevron); stroke-width: 2.25; }
a.ui-fila:active, label.ui-fila:active, button.ui-fila:active { background: var(--c-marina-suave); }
button.ui-fila { width: 100%; text-align: left; }
.ui-fila[aria-disabled="true"] .ui-fila-tit { color: var(--c-texto-2); }

/* Fila de plan resumido: una línea tocable con todo el plan dentro.
   El resumen nunca baja a una segunda línea: si no cabe, se corta y el detalle está a un toque */
.ui-plan > span { min-width: 0; }
.ui-plan .ui-fila-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-plan .ui-fila-sub b { font-weight: var(--p-texto); color: var(--c-texto); }

/* ---------- 9. Controles ---------- */
/* Interruptor: encendido en turquesa hondo (--c-encendido) con el pomo hueso (5,4:1). Con turquesa claro el pomo
   se quedaba en 1,6:1. En el pase de lista, «presente» se lee verde mar: es lo bueno */
.ui-switch { appearance: none; -webkit-appearance: none; position: relative; flex: none; width: 51px; height: 31px; margin: 0;
  border-radius: var(--r-pastilla); background: var(--c-arena-2); box-shadow: inset 0 0 0 1px rgb(var(--c-tinta-rgb) / .14); cursor: pointer;
  transition: background-color 220ms var(--ease-out); }
.ui-switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 27px; height: 27px; border-radius: var(--r-redondo);
  background: var(--c-hueso); box-shadow: 0 2px 4px rgb(var(--c-tinta-rgb) / .22), 0 0 0 .5px rgb(var(--c-tinta-rgb) / .06);
  transition: transform 220ms var(--ease-out); }
.ui-switch:checked, .ui-switch[aria-checked="true"] { background: var(--c-encendido); box-shadow: none; }
.ui-switch:checked::after, .ui-switch[aria-checked="true"]::after { transform: translateX(20px); }
.ui-switch:disabled { opacity: .45; cursor: default; }
/* También sobre <button role="switch" aria-checked> (catálogo, extraescolares, contenido): mismo aspecto, sin <label for> */
button.ui-switch { padding: 0; border: 0; }
.ui-switch:focus-visible { outline: 2px solid var(--c-foco); outline-offset: 2px; }

/* Stepper: − 1 + en un carril; cada botón tiene 44 px de toque */
.ui-stepper { display: inline-flex; align-items: center; height: 36px; border-radius: var(--r-carril); background: var(--c-arena); flex: none; }
.ui-stepper button { position: relative; display: grid; place-items: center; width: 44px; height: 36px; color: var(--c-marina); }
.ui-stepper button::after { content: ""; position: absolute; inset: -4px 0; }
.ui-stepper button:disabled { color: rgb(var(--c-tinta-rgb) / .3); cursor: default; }
.ui-stepper output { min-width: 20px; text-align: center; font-weight: var(--p-fuerte); }

/* Elegir una opción (forma de pago, filtro «Todas ▾», packs, idioma): fila del grupo con check a la derecha
   (sin círculos de radio). La elegida: la fila se tiñe de turquesa suave y el check es un círculo turquesa de
   22 px con check tinta y anillo hondo (4,9:1 sobre el suave). El título pasa a 700: el estado no depende
   solo del color. Las filas que no están elegidas no cambian */
.ui-opcion { cursor: pointer; padding-block: 8px; }
.ui-opcion input { position: absolute; opacity: 0; pointer-events: none; }
.ui-opcion .ui-check { width: 22px; height: 22px; padding: 4px; border-radius: var(--r-redondo); background: var(--c-turquesa);
  color: var(--c-tinta); stroke-width: 3; box-shadow: inset 0 0 0 1.5px var(--c-turquesa-h); opacity: 0;
  transition: opacity var(--dur-estado) var(--ease-out); }
.ui-fila.ui-opcion:has(input:checked), .ui-fila.ui-opcion--elegida { background: var(--c-turquesa-suave); }
.ui-opcion:has(input:checked) .ui-check, .ui-opcion--elegida .ui-check { opacity: 1; }
.ui-opcion:has(input:checked) .ui-fila-tit, .ui-opcion--elegida .ui-fila-tit { font-weight: var(--p-fuerte); }
/* .ui-opcion--elegida: respaldo para WebView sin :has() (Chrome < 105, Safari < 15.4). El JS que pinta opciones la pone en la elegida */
.ui-opcion:has(input:focus-visible) { outline: 2px solid var(--c-foco); outline-offset: -2px; }
.ui-opcion:has(input:disabled) { cursor: default; }

/* Segmentado (dos o tres opciones cortas, p. ej. «Tarjeta | En la playa») */
.ui-seg { display: grid; grid-auto-columns: minmax(0, 1fr); grid-auto-flow: column; padding: 2px; border-radius: var(--r-carril); background: var(--c-arena-2); }
.ui-seg button { min-height: 40px; border-radius: var(--r-seg); font-size: var(--fs-15); color: var(--c-texto-2);
  transition: background-color var(--dur-estado) var(--ease-out), color var(--dur-estado) var(--ease-out); }
/* Elegido: la receta del día de la tira. Turquesa con tinta 700 (10,4:1) y anillo hondo de 1 px (4,5:1 contra
   el carril arena 2), para que se vea también en gris. Vale para botones, pestañas, radios, enlaces (§ 18.3, 24.13) */
.ui-seg button[aria-pressed="true"], .ui-seg [role="tab"][aria-selected="true"] { background: var(--c-turquesa); color: var(--c-tinta); font-weight: var(--p-fuerte);
  box-shadow: inset 0 0 0 1px var(--c-turquesa-h); }
/* Como pestañas (role="tablist"): cada botón role="tab" con aria-selected; el panel, role="tabpanel" */

/* ---------- 10. Barra inferior: 4 pestañas; la activa, con cápsula turquesa y rótulo marina ---------- */
.ui-barra { position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-barra); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  height: calc(var(--alto-barra) + var(--zona-abajo)); padding-bottom: var(--zona-abajo);
  background: var(--c-hueso); box-shadow: 0 -1px 0 var(--c-regla); }
.ui-barra-item { position: relative; isolation: isolate; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 44px; color: var(--c-hormigon); font-size: var(--fs-12); font-weight: var(--p-texto); }
.ui-barra-item .ui-ico { width: 24px; height: 24px; }
/* Dónde estás: cápsula turquesa de 52 × 28 detrás del icono. Icono y rótulo siguen en marina 700 (7,2:1 sobre
   turquesa; 11,6:1 sobre hueso); el estado lo dicen también el peso y aria-current, así que va sin anillo.
   Centrada en el icono (24 + 3 + 16,2 = 43,2 → su centro queda 9,6 px por encima del del item); vale igual
   en el raíl de escritorio. Sin movimiento: viene puesta en el HTML de cada página y, al tocar otra pestaña, salta a
   ella en el mismo clic (utils/barra.js) */
.ui-barra-item[aria-current="page"]::before { content: ""; position: absolute; z-index: -1; left: 50%; top: calc(50% - 9.6px - 14px);
  width: 52px; height: 28px; transform: translateX(-50%); border-radius: var(--r-pastilla); background: var(--c-turquesa); }
.ui-barra-item[aria-current="page"] { color: var(--c-marina); font-weight: var(--p-fuerte); }
.ui-barra-item[aria-current="page"] .ui-ico { stroke-width: 2.25; }
/* Barra del equipo de un monitor sin admin: «Buscar» (data-solo-admin) va oculta y quedan 3 */
.ui-barra:has(> .ui-barra-item[hidden]) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* Al tocar una pestaña que abre otra página (utils/barra.js, 24-sep-2026): en el mismo clic la cápsula ya
   está en la tocada y lo demás se apaga hasta que llega la página nueva (0,8-1,3 s en 3G), sin poder tocarse.
   La barra sigue viva: otro toque cambia de destino. De golpe, sin fundido: la navegación sale en el
   fotograma siguiente y el navegador se queda con ESE fotograma hasta que pinta la página nueva (un fundido
   se congelaría al empezar, sin apagarse). Nada que apagar en html.lowend ni con movimiento reducido.
   Opacidad .7, no .4: con la página precargada, en wifi y 4G el apagado dura 70-250 ms y al 40 % se veía como
   un parpadeo gris (claro → gris → pantalla nueva); al 70 % es un pulso, y en 3G (≈0,6 s) sigue avisando */
html.ui-navegando body.con-barra > :not(.ui-barra) { opacity: .7; pointer-events: none; }

/* ---------- 11. Acción fija abajo (reserva y pago) ---------- */
.ui-accion { position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-barra); padding: 12px var(--margen) calc(var(--zona-abajo) + 6px);
  background: var(--c-hueso); box-shadow: 0 -1px 0 var(--c-regla), var(--sombra-arriba); }
.ui-accion-fila { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 20px; }
.ui-total { display: block; min-width: 72px; }
.ui-total small { display: block; font-size: var(--fs-13); color: var(--c-texto-2); }
.ui-cifra { font: var(--p-display) var(--fs-28)/1.05 var(--f-display); color: var(--c-tinta); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.ui-accion-nota { margin-top: 4px; text-align: center; font-size: var(--fs-13); color: var(--c-texto-2); }

/* ---------- 12. Hoja inferior (filtro de actividad, desglose, plan) ---------- */
.ui-velo { position: fixed; inset: 0; z-index: var(--z-hoja); background: var(--c-velo); animation: ui-funde var(--dur-hoja) var(--ease-out); }
.ui-hoja { position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-hoja); margin: 0 auto; max-width: 640px;
  max-height: calc(100vh - 12px - var(--zona-arriba)); max-height: calc(100dvh - 12px - var(--zona-arriba)); overflow-y: auto; overscroll-behavior: contain;
  animation: ui-sube var(--dur-hoja) var(--ease-drawer); padding: 8px var(--margen) calc(var(--zona-abajo) + 12px);
  background: var(--c-hueso); border-radius: var(--r-hoja) var(--r-hoja) 0 0; box-shadow: var(--sombra-hoja); }
.ui-hoja-asa { display: block; width: 36px; height: 5px; margin: 0 auto 12px; border-radius: 3px; background: rgb(var(--c-tinta-rgb) / .18); }
.ui-hoja-cab { display: flex; align-items: center; justify-content: space-between; min-height: 44px; margin-bottom: 4px; }
.ui-hoja-tit { font-size: var(--fs-20); font-weight: var(--p-fuerte); }
.ui-hoja .ui-grupo { background: var(--c-arena); }
/* Dentro de una hoja el grupo es arena: el carril del stepper pasa a arena 2 para seguir viéndose */
.ui-hoja .ui-stepper { background: var(--c-arena-2); }

/* ---------- 13. Esqueleto y vacío ---------- */
.ui-esq { display: block; height: 14px; border-radius: var(--r-pastilla); background: var(--c-arena-2); position: relative; overflow: hidden; }
.ui-esq::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgb(var(--c-hueso-rgb) / .7), transparent);
  animation: ui-brillo 1.4s var(--ease-out) infinite; }
@keyframes ui-brillo { to { transform: translateX(100%); } }
.ui-esq-fila { display: grid; grid-template-columns: 52px minmax(0, 1fr) 40px; gap: 12px; align-items: center; min-height: var(--fila);
  border-bottom: 1px solid var(--c-regla); }
.ui-vacio { padding: 40px 12px; text-align: center; }
.ui-vacio > .ui-ico { width: 28px; height: 28px; color: var(--c-marina); margin-bottom: 12px; } /* hijo directo (F15): no alcanza al icono de «Reintentar» */
.ui-vacio-tit { font-size: var(--fs-17); font-weight: var(--p-fuerte); }
.ui-vacio-txt { margin: 4px auto 16px; max-width: 30ch; font-size: var(--fs-15); color: var(--c-texto-2); }

/* ---------- 14. Gama baja y movimiento reducido ----------
   (el apagado general está en la CAPA 1b; aquí solo lo propio de los componentes) */
html.lowend .ui-esq::after { display: none; }
html.lowend .ui-hoja, html.lowend .ui-accion { box-shadow: 0 -1px 0 var(--c-regla); }
@media (prefers-reduced-motion: reduce) {
  .ui-hoja { animation-name: ui-funde; }
  .ui-esq::after { display: none; }
  .ui-olas i { animation-duration: 2.4s; }
  .ui-btn:active, .ui-btn-ico:active { transform: none; }
}

/* ---------- 15. Cabecera de la reserva ---------- */
.ui-res-cab { margin-top: 0; }
.ui-res-cab .ui-proxima-cuando { margin-top: 6px; color: var(--c-texto); }
.ui-res-cond { display: flex; flex-wrap: wrap; align-items: center; column-gap: 4px; margin-top: 2px; font-size: var(--fs-15); color: var(--c-texto-2); }
.ui-res-cond .ui-enlace { position: relative; min-height: 32px; font-size: var(--fs-15); }
.ui-res-cond .ui-enlace::after { content: ""; position: absolute; inset: -6px -4px; } /* 44 px de toque */

/* =====================================================================
   16. MONITOR (entrega 2)
   ===================================================================== */
/* Insignia con número (avisos del monitor) */
.ui-insignia--num { top: 5px; right: 4px; width: auto; min-width: 18px; height: 18px; padding: 0 5px;
  display: grid; place-items: center; font: var(--p-fuerte) var(--fs-12)/1 var(--f-cuerpo); color: var(--c-accion-txt); } /* 12 px: nada por debajo del mínimo */

/* Tarjeta «Ahora»: la ÚNICA superficie marina del inicio del monitor.
   Texto hueso; lo secundario, hueso con transparencia (nunca gris sobre color) */
.ui-ahora {
  background: var(--c-marina); color: var(--c-accion-txt); border-radius: var(--r-control); padding: 6px 6px 6px; }
.ui-ahora-cab { display: block; padding: 12px 12px 14px; border-radius: var(--r-carril); color: inherit;
  transition: background-color var(--dur-estado) var(--ease-out); }
.ui-ahora-cab:active { background: rgb(var(--c-hueso-rgb) / .08); }
/* La cuenta atrás en turquesa 700 (7,2:1 sobre marina), con halo en su punto: marina + turquesa, como la web */
.ui-ahora-cuando { display: flex; align-items: center; gap: 8px; font-size: var(--fs-15); font-weight: var(--p-fuerte); color: var(--c-turquesa); }
.ui-ahora-cuando::before { content: ""; width: 8px; height: 8px; border-radius: var(--r-redondo); background: var(--c-turquesa); flex: none;
  box-shadow: 0 0 0 3px rgb(var(--c-turquesa-rgb) / .28); }
.ui-ahora-tit { margin-top: 6px; font-size: var(--fs-22); font-weight: var(--p-fuerte); line-height: 1.2; }
.ui-ahora-tit .ui-hora { color: inherit; }
.ui-ahora-det { margin-top: 4px; font-size: var(--fs-15); color: var(--c-texto-sobre-marina-2); }
.ui-ahora-det b { font-weight: var(--p-texto); color: var(--c-accion-txt); }
.ui-ahora :focus-visible { outline-color: var(--c-hueso); }   /* hueso, para que no se funda con «Pasar lista» */
/* Botón claro: hueso sobre marina (11,6:1) */
.ui-btn--claro { background: var(--c-hueso); color: var(--c-marina); }
.ui-ahora > .ui-btn { width: auto; margin: 2px 12px 12px; }
.ui-btn--claro:active { background: var(--c-arena-2); }
@media (hover: hover) and (pointer: fine) { .ui-btn--claro:hover { background: var(--c-arena); } }
/* «Pasar lista», la acción de la tarjeta: turquesa con texto tinta (10,4:1; 7,2:1 contra el marina). Es LA acción
   del día: la única alegría en las pantallas del equipo */
.ui-ahora > .ui-btn--claro { background: var(--c-turquesa); color: var(--c-tinta); }
.ui-ahora > .ui-btn--claro:active { background: var(--c-turquesa-pulsado); }
@media (hover: hover) and (pointer: fine) { .ui-ahora > .ui-btn--claro:hover { background: var(--c-turquesa-pulsado); } }

/* Fila de clase del monitor: en vez de precio, cuántos alumnos */
.ui-clase-cuantos { font-size: var(--fs-15); color: var(--c-texto-2); white-space: nowrap; }

/* ---------- 17. Hoja alta (pase de lista) ---------- */
.ui-hoja--alta { top: calc(var(--zona-arriba) + 7px); max-height: none; display: flex; flex-direction: column; padding-bottom: 0; }
.ui-hoja--alta .ui-hoja-cab { align-items: flex-start; margin-bottom: 0; }
.ui-hoja-sub { margin-top: 2px; font-size: var(--fs-15); color: var(--c-texto-2); }
.ui-hoja-sub b { font-weight: var(--p-fuerte); color: var(--c-tinta); }
.ui-hoja-lista { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; margin: 12px calc(-1 * var(--margen)) 0;
  padding: 0 var(--margen); border-top: 1px solid var(--c-regla); }
.ui-hoja-lista::-webkit-scrollbar { display: none; }
.ui-hoja-pie { padding: 12px 0 calc(var(--zona-abajo) + 8px); box-shadow: 0 -1px 0 var(--c-regla); }
.ui-hoja .ui-btn-ico--cerrar { color: var(--c-texto-2); margin: -4px -10px 0 0; }

/* Fila del pase: nombre + una línea · llamar · interruptor */
.ui-pase { display: grid; grid-template-columns: minmax(0, 1fr) var(--toque) 59px; align-items: center; column-gap: 4px;
  min-height: 64px; border-bottom: 1px solid var(--c-regla); }
.ui-pase:last-child { border-bottom: 0; }
.ui-pase-quien { display: block; min-width: 0; min-height: var(--toque); padding: 8px 0; text-align: left; border-radius: var(--r-seg);
  transition: background-color var(--dur-estado) var(--ease-out); }
.ui-pase-quien:active { background: var(--c-marina-suave); }
.ui-pase-nom { display: block; font-size: var(--fs-17); font-weight: var(--p-texto); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-pase-nom small { margin-left: 4px; font-size: var(--fs-15); color: var(--c-texto-2); }
.ui-pase-det { display: block; margin-top: 1px; font-size: var(--fs-15); color: var(--c-texto-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-pase-det .ui-etq--aviso { font-weight: var(--p-fuerte); }
.ui-pase .ui-btn-ico { color: var(--c-texto-2); }
.ui-pase .ui-btn-ico .ui-ico { width: 20px; height: 20px; }
.ui-pase-si { display: grid; place-items: center end; min-height: 56px; cursor: pointer; }
/* Presente: el nombre pasa a tinta plena; ausente se queda igual (no se castiga a nadie) */
.ui-pase:has(.ui-switch:checked) .ui-pase-nom { font-weight: var(--p-fuerte); }

/* =====================================================================
   18. PIEZAS QUE FALTABAN (fundación, 23-sep-2026)
   Mismo lenguaje que lo aprobado: neutros, marina, reglas finas, sin cajas
   de más. Espécimen de estas piezas: piezas.html (misma carpeta).
   ===================================================================== */

/* ---------- 18.1 Movimiento de hojas, diálogo y botón ocupado ---------- */
@keyframes ui-sube { from { transform: translateY(100%); } }
@keyframes ui-funde { from { opacity: 0; } }
@keyframes ui-entra-lado { from { transform: translateX(100%); } }
@keyframes ui-ola { 0%, 100% { transform: scaleY(.45); opacity: .55; } 50% { transform: scaleY(1); opacity: 1; } }

/* ---------- 18.2 Botones: compacto, peligro, Google, ocupado, motivo ---------- */
.ui-btn--compacto { min-height: var(--control-c); padding: 0 14px; font-size: var(--fs-15); }
/* Peligro: relleno de error SOLO dentro del diálogo que confirma algo destructivo */
.ui-btn--peligro { background: var(--c-error); color: var(--c-hueso); }
.ui-btn--peligro:active { background: var(--c-error-hondo); }
/* Google: la única marca ajena (entrada). Blanco con borde gris y la «G» de colores (#i-google) */
.ui-btn--google { background: var(--c-google-fondo); color: var(--c-google-texto); box-shadow: inset 0 0 0 1px var(--c-google-borde); }
.ui-btn--google .ui-ico { width: 20px; height: 20px; stroke: none; }
/* Ocupado: aria-busy + tres barras de marea + el verbo en curso («Reservando…»). No se puede volver a tocar */
.ui-btn[aria-busy="true"] { cursor: progress; transform: none; }
.ui-btn--principal[aria-busy="true"] { background: var(--c-accion); color: var(--c-accion-txt); }
.ui-btn--secundario[aria-busy="true"] { background: var(--c-marina-suave); color: var(--c-marina); }
.ui-olas { display: inline-flex; gap: 3px; align-items: center; height: 18px; }
.ui-olas i { display: block; width: 3px; height: 16px; border-radius: 2px; background: currentColor;
  animation: ui-ola 1s var(--ease-in-out) infinite; }
.ui-olas i:nth-child(2) { animation-delay: .12s; }
.ui-olas i:nth-child(3) { animation-delay: .24s; }
/* Motivo bajo un botón deshabilitado: dice qué falta */
.ui-motivo { display: block; margin: 8px 2px 0; font-size: var(--fs-13); line-height: 1.4; color: var(--c-texto-2); text-align: center; }
/* Enlace: variantes */
.ui-enlace--peligro { color: var(--c-error); }
.ui-enlace[aria-disabled="true"], .ui-enlace:disabled { color: var(--c-texto-2); pointer-events: none; }
/* Botón de icono deshabilitado */
.ui-btn-ico:disabled, .ui-btn-ico[aria-disabled="true"] { color: var(--c-hormigon); background: none; cursor: default; }
/* Dos botones a la par (decisión del mar, diálogos en escritorio) y pila de acciones */
.ui-par { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.ui-acciones { display: grid; gap: 8px; }

/* ---------- 18.3 Campos ---------- */
.ui-campo { display: grid; gap: 6px; }
.ui-campo + .ui-campo, .ui-campo-fila + .ui-campo, .ui-campo + .ui-campo-fila { margin-top: 16px; }
.ui-campo-etq { font-size: var(--fs-15); font-weight: var(--p-fuerte); line-height: 1.3; color: var(--c-tinta); }
.ui-campo-etq small { font-size: var(--fs-15); font-weight: var(--p-texto); color: var(--c-texto-2); }
.ui-campo-caja { position: relative; }
.ui-campo-ctrl { display: block; width: 100%; min-height: var(--control); padding: 0 14px; margin: 0;
  border-radius: var(--r-control); background: var(--c-hueso); border: 1px solid var(--c-borde-campo); color: var(--c-tinta);
  font: var(--p-texto) max(16px, var(--fs-17))/1.3 var(--f-cuerpo); -webkit-appearance: none; appearance: none;
  transition: border-color var(--dur-estado) var(--ease-out), box-shadow var(--dur-estado) var(--ease-out); }
textarea.ui-campo-ctrl { min-height: 104px; padding: 12px 14px; line-height: 1.45; resize: vertical; }
.ui-campo-ctrl::placeholder { color: var(--c-hormigon); opacity: 1; }
.ui-campo-ctrl:focus { outline: none; border-color: var(--c-foco); box-shadow: 0 0 0 3px var(--c-turquesa-halo); }   /* borde hondo (el indicador, ≥ 3:1) y halo decorativo */
.ui-campo-ctrl:disabled, .ui-campo-ctrl[readonly] { background: var(--c-arena-2); border-color: transparent; color: var(--c-texto-2); }
select.ui-campo-ctrl { padding-right: 44px; background-repeat: no-repeat; background-position: right 14px center; background-size: 16px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230B3267' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6l6 -6'/%3E%3C/svg%3E"); }
.ui-campo-ctrl--icono { padding-right: 52px; }
.ui-campo-caja > .ui-btn-ico { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); }
.ui-campo-ayuda { font-size: var(--fs-13); line-height: 1.4; color: var(--c-texto-2); }
.ui-campo--error .ui-campo-ctrl { border: 2px solid var(--c-error); padding-left: 13px; }
.ui-campo-error { display: flex; gap: 6px; align-items: flex-start; font-size: var(--fs-13); font-weight: var(--p-fuerte); line-height: 1.4; color: var(--c-error); }
.ui-campo-error .ui-ico { width: 16px; height: 16px; margin-top: 1px; }
.ui-campo-fila { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 380px) { .ui-campo-fila { grid-template-columns: minmax(0, 1fr); } }
/* Casilla con su texto; el enlace legal va FUERA de la etiqueta */
.ui-casilla { display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 12px; align-items: start; min-height: var(--toque); padding: 10px 0; cursor: pointer; }
.ui-casilla input, .ui-fila--casilla > input[type="checkbox"] { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; margin: 0; border-radius: var(--r-seg);
  border: 1.5px solid var(--c-hormigon); background: var(--c-hueso) center / 16px no-repeat; cursor: pointer;
  transition: background-color var(--dur-estado) var(--ease-out), border-color var(--dur-estado) var(--ease-out); }
/* Marcada: turquesa, borde hondo (5,4:1) y check tinta (10,4:1) */
.ui-casilla input:checked, .ui-fila--casilla > input[type="checkbox"]:checked { border-color: var(--c-turquesa-h); background-color: var(--c-turquesa);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230E1621' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5l10 -10'/%3E%3C/svg%3E"); }
.ui-casilla input:disabled, .ui-fila--casilla > input[type="checkbox"]:disabled { opacity: 1; background-color: var(--c-arena-2); border-color: var(--c-regla-fuerte); cursor: default; }
.ui-casilla-txt { font-size: var(--fs-15); line-height: 1.45; }
/* Segmentado con radios (formularios): mismo aspecto que el de botones con aria-pressed */
.ui-seg label { position: relative; display: grid; cursor: pointer; }
.ui-seg label input { position: absolute; opacity: 0; pointer-events: none; }
.ui-seg label span { display: grid; place-items: center; min-height: 40px; padding: 0 8px; border-radius: var(--r-seg); font-size: var(--fs-15); color: var(--c-texto-2);
  text-align: center; transition: background-color var(--dur-estado) var(--ease-out), color var(--dur-estado) var(--ease-out); }
.ui-seg label input:checked + span { background: var(--c-turquesa); color: var(--c-tinta); font-weight: var(--p-fuerte); box-shadow: inset 0 0 0 1px var(--c-turquesa-h); }
.ui-seg label input:focus-visible + span { outline: 2px solid var(--c-foco); outline-offset: -2px; }

/* ---------- 18.4 Aviso en línea: junto a lo que lo provoca, uno solo a la vez ----------
   Sin franja lateral, sin título en mayúsculas. Info = hueso; aviso/error/ok = su fondo. */
.ui-aviso { display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: 12px; align-items: start; padding: 12px 14px;
  border-radius: var(--r-control); background: var(--c-superficie); color: var(--c-texto); font-size: var(--fs-15); line-height: 1.45; }
.ui-aviso > .ui-ico { margin-top: 1px; color: var(--c-marina); }
.ui-aviso-tit { display: block; font-weight: var(--p-fuerte); }
.ui-aviso p { color: var(--c-texto-2); }
.ui-aviso .ui-enlace { min-height: 36px; margin-bottom: -8px; }
.ui-aviso--aviso { background: var(--c-aviso-fondo); }
.ui-aviso--aviso > .ui-ico, .ui-aviso--aviso .ui-aviso-tit { color: var(--c-aviso); }
.ui-aviso--error { background: var(--c-error-fondo); }
.ui-aviso--error > .ui-ico, .ui-aviso--error .ui-aviso-tit { color: var(--c-error); }
.ui-aviso--ok { background: var(--c-ok-fondo); }
.ui-aviso--ok > .ui-ico, .ui-aviso--ok .ui-aviso-tit { color: var(--c-ok); }
.ui-aviso--aviso p, .ui-aviso--error p, .ui-aviso--ok p { color: var(--c-tinta); }

/* ---------- 18.5 Diálogo de confirmación (solo lo destructivo) ---------- */
.ui-dialogo-velo { position: fixed; inset: 0; z-index: var(--z-dialogo); display: grid; place-items: center; padding: 24px;
  background: var(--c-velo); animation: ui-funde var(--dur-estado) var(--ease-out); }
.ui-dialogo { width: 100%; max-width: 360px; padding: 20px 20px 16px; border-radius: var(--r-control); background: var(--c-hueso);
  box-shadow: var(--sombra-flota); animation: ui-funde var(--dur-estado) var(--ease-out); }
.ui-dialogo-tit { font-size: var(--fs-20); font-weight: var(--p-fuerte); line-height: 1.25; }
.ui-dialogo-txt { margin-top: 8px; font-size: var(--fs-15); line-height: 1.5; color: var(--c-texto-2); }
.ui-dialogo .ui-campo { margin-top: 16px; }
.ui-dialogo-pie { display: grid; gap: 8px; margin-top: 20px; }

/* ---------- 18.6 Aviso flotante (lo pinta utils/toast.js) ---------- */
.ui-toasts { position: fixed; left: var(--margen); right: var(--margen); z-index: var(--z-aviso); display: grid; gap: 8px; justify-items: center;
  bottom: calc(var(--alto-barra) + var(--alto-accion) + var(--zona-abajo) + 12px); pointer-events: none; }
.ui-toast { pointer-events: auto; width: 100%; max-width: 480px; display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; align-items: center; gap: 12px;
  min-height: 52px; padding: 4px 4px 4px 16px; border-radius: var(--r-control); background: var(--c-tinta); color: var(--c-hueso);
  box-shadow: var(--sombra-flota); font-size: var(--fs-15); line-height: 1.4; animation: ui-funde var(--dur-estado) var(--ease-out); }
.ui-toast > .ui-ico { color: var(--c-hueso); }
.ui-toast--ok > .ui-ico { color: var(--c-ok-claro); }
.ui-toast--aviso > .ui-ico { color: var(--c-aviso-claro); }
.ui-toast--error > .ui-ico { color: var(--c-error-claro); }
.ui-toast-acc { display: flex; align-items: center; }
.ui-toast-acc button { display: grid; place-items: center; min-height: var(--toque); min-width: var(--toque); padding: 0 12px;
  -webkit-appearance: none; appearance: none; margin: 0; border: 0; background: none; font: inherit; cursor: pointer; /* F7: el aviso cuelga de <body>, fuera de .v2 si la página no lo lleva en body */
  color: var(--c-hueso); font-weight: var(--p-fuerte); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.ui-toast-acc .ui-toast-cerrar { padding: 0; text-decoration: none; color: var(--c-texto-sobre-marina-2); }
/* F7: el texto que pinta toast.js (solo texto; un enlace solo con {html: true}) y la salida. */
.ui-toast-texto { min-width: 0; overflow-wrap: anywhere; }
.ui-toast-texto a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.ui-toast--sale { opacity: 0; transition: opacity var(--dur-estado) var(--ease-out); }

/* ---------- 18.7 Estados: esqueleto (leyendo) · vacío (no hay) · error (no ha contestado) ---------- */
.ui-esq--corto { width: 40%; }
.ui-esq--medio { width: 64%; }
.ui-esq--bloque { height: 112px; border-radius: var(--r-control); }
.ui-vacio--error > .ui-ico { color: var(--c-aviso); }
/* Cifra que no se ha podido leer, dentro de una fila: nunca «0,00 €» ni «—» */
.ui-fila--error .ui-fila-sub { color: var(--c-aviso); }

/* ---------- 18.8 Filas: peligro, avatar, punto de actividad, clase cancelada ---------- */
.ui-fila--peligro .ui-fila-tit, .ui-fila--peligro > .ui-ico { color: var(--c-error); }
.ui-fila--av { grid-template-columns: 40px minmax(0, 1fr) auto; min-height: 64px; }
.ui-fila--av + .ui-fila::before, .ui-fila--av + .ui-fila--av::before { left: 70px; }
.ui-av { display: grid; place-items: center; width: 40px; height: 40px; flex: none; border-radius: var(--r-redondo); overflow: hidden;
  background: var(--c-arena-2); color: var(--c-marina); font-size: var(--fs-15); font-weight: var(--p-fuerte); letter-spacing: .02em; }
.ui-av img { width: 100%; height: 100%; object-fit: cover; }
.ui-av--grande { width: 72px; height: 72px; font-size: var(--fs-22); }
/* Tu avatar sin foto: turquesa con las iniciales (o el muñeco) en marina, 7,2:1. SOLO el tuyo (Cuenta,
   «Mi perfil» del equipo): en las listas de alumnos y en las fichas del equipo sigue en arena 2 */
.ui-av--tuyo { background: var(--c-turquesa); color: var(--c-marina); }
/* Bolita de actividad: 8 px (10 en la hoja «Todas ▾» y en el título de la reserva), junto al NOMBRE de la
   actividad en alumno y equipo (notas de Nacho, 23-sep). Decorativa: siempre aria-hidden, el nombre ya lo
   dice. La clase la da puntoTipo()/puntoHTML() de utils/tipos-clase.js; nunca a mano por tipo. */
.ui-punto { display: inline-block; width: 8px; height: 8px; flex: none; border-radius: var(--r-redondo); background: var(--punto, var(--c-tinta-2)); vertical-align: middle; }
.ui-punto--surf { --punto: var(--c-act-surf); }
.ui-punto--paddle { --punto: var(--c-act-paddle); }
.ui-punto--yoga { --punto: var(--c-act-yoga); }
.ui-punto--skate { --punto: var(--c-act-skate); }
.ui-punto--kids, .ui-punto--extra { --punto: var(--c-act-kids); }
.ui-punto--entreno { --punto: var(--c-act-entreno); }
.ui-punto--natacion { --punto: var(--c-act-natacion); }
/* «Todas» en la hoja del filtro: las cuatro de siempre, en quesitos */
.ui-punto--todas { background: conic-gradient(var(--c-act-surf) 0 25%, var(--c-act-skate) 0 50%, var(--c-act-paddle) 0 75%, var(--c-act-yoga) 0); }
/* Clase cancelada: nombre tachado y la nota «Cancelada» en error; no se toca */
.ui-clase--cancelada { color: var(--c-texto-2); pointer-events: none; }
.ui-clase--cancelada .ui-hora { color: var(--c-texto-2); font-weight: var(--p-texto); }
.ui-clase--cancelada .ui-clase-nombre { text-decoration: line-through; text-decoration-thickness: 1px; }
/* Nota de clase para el equipo: punto de actividad delante del nombre */
.ui-clase-nombre .ui-punto { margin: -2px 8px 0 0; }

/* ---------- 18.9 Lectura y desplegables (privacidad, condiciones) ---------- */
.ui-lectura { max-width: var(--medida); font-weight: var(--p-lectura); line-height: var(--lh-lectura); }
.ui-lectura h2 { margin: var(--bloque) 0 8px; font-size: var(--fs-20); font-weight: var(--p-fuerte); line-height: 1.25; }
.ui-lectura p + p { margin-top: 12px; }
.ui-lectura a { color: var(--c-enlace); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.ui-acordeon { border-bottom: 1px solid var(--c-regla); }
.ui-acordeon > summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: var(--fila-lista);
  padding: 10px 0; font-weight: var(--p-fuerte); list-style: none; cursor: pointer; }
.ui-acordeon > summary::-webkit-details-marker { display: none; }
.ui-acordeon > summary .ui-ico { width: 16px; height: 16px; color: var(--c-chevron); stroke-width: 2.25; transition: transform var(--dur-estado) var(--ease-out); }
.ui-acordeon[open] > summary .ui-ico { transform: rotate(180deg); }
.ui-acordeon > :not(summary):not(.ui-acordeon-cab) { padding-bottom: 16px; color: var(--c-texto-2); font-size: var(--fs-15); line-height: 1.5; }

/* ---------- 18.10 Banda de sistema (sin conexión): empuja, no tapa ---------- */
.ui-banda { position: sticky; top: 0; z-index: var(--z-cabecera); display: flex; align-items: center; gap: 10px;
  padding: calc(var(--zona-arriba) + 8px) var(--margen) 8px; background: var(--c-tinta); color: var(--c-hueso); font-size: var(--fs-15); }
.ui-banda .ui-ico { color: var(--c-aviso-claro); }

/* ---------- 18.11 Formularios de admin ---------- */
.ui-form { display: grid; gap: 16px; }
.ui-form > .ui-campo + .ui-campo { margin-top: 0; }
.ui-form-grupo-tit { margin-top: 16px; font-size: var(--fs-17); font-weight: var(--p-fuerte); }
.ui-form-pie { display: grid; gap: 8px; margin-top: 8px; }
@media (min-width: 720px) {
  .ui-form--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 16px; }
  .ui-form--2 > .ui-form-ancho, .ui-form--2 > .ui-form-grupo-tit, .ui-form--2 > .ui-form-pie { grid-column: 1 / -1; }
  .ui-form-pie { display: flex; justify-content: flex-end; }
  .ui-form-pie .ui-btn { min-width: 160px; }
}

/* ---------- 18.12 Escritorio (≥ 1024 px con ratón): raíl, tabla, maestro-detalle, panel lateral ---------- */
.ui-tabla { width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0; background: var(--c-superficie);
  border-radius: var(--r-control); overflow: hidden; overflow: clip; font-size: var(--fs-15); }
/* overflow: clip (y no hidden) para que la cabecera sticky se pegue de verdad: hidden crea un contenedor de scroll y la deja quieta */
.ui-tabla th { position: sticky; top: 0; z-index: 1; height: 40px; padding: 0 12px; text-align: left; background: var(--c-hueso);
  font-size: var(--fs-13); font-weight: var(--p-fuerte); color: var(--c-texto-2); border-bottom: 1px solid var(--c-regla-fuerte); white-space: nowrap; }
.ui-tabla td { height: var(--fila); padding: 0 12px; vertical-align: middle; border-top: 1px solid var(--c-regla);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-tabla tbody tr:first-child td { border-top: 0; }
.ui-tabla td.ui-num, .ui-tabla th.ui-num { text-align: right; font-variant-numeric: tabular-nums; }
.ui-tabla .ui-hora { font-size: var(--fs-15); }
.ui-tabla tr.ui-tabla-dia td { height: auto; padding: 16px 12px 6px; background: var(--c-fondo); font-weight: var(--p-fuerte); color: var(--c-texto-2); }
.ui-tabla tbody tr[data-href], .ui-tabla tbody tr[tabindex] { cursor: pointer; }
.ui-tabla tbody tr[aria-selected="true"] td { background: var(--c-marina-suave); }
@media (hover: hover) and (pointer: fine) {
  .ui-tabla tbody tr[data-href]:hover td, .ui-tabla tbody tr[tabindex]:hover td { background: var(--c-marina-suave); }
}
.ui-maestro-detalle { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; }
@media (min-width: 1024px) and (pointer: fine) {
  .v2 .ui-contenido--ancho { max-width: 1200px; }
  .ui-maestro-detalle { grid-template-columns: minmax(0, 1fr) 420px; align-items: start; }
  .ui-maestro-detalle > aside { position: sticky; top: 24px; }
  /* La barra de pestañas pasa a raíl a la izquierda */
  .v2.con-barra { --alto-barra: 0px; padding-left: 88px; }
  .ui-barra { top: 0; right: auto; width: 88px; height: auto; padding: calc(var(--zona-arriba) + 16px) 0 0; grid-template-columns: none;
    grid-auto-rows: 72px; align-content: start; box-shadow: 1px 0 0 var(--c-regla); }
  .ui-barra:has(> .ui-barra-item[hidden]) { grid-template-columns: none; }
  /* La hoja de detalle entra por la derecha */
  .ui-hoja--lateral { top: 0; left: auto; width: 440px; max-width: none; max-height: none; border-radius: 0;
    box-shadow: var(--sombra-flota); animation-name: ui-entra-lado; display: flex; flex-direction: column; }
  .ui-hoja--lateral .ui-hoja-asa { display: none; }
}

/* ---------- 18.13 Remates: hover con ratón, pulsado de pestaña, huecos fijos, segmentado sobre foto ---------- */
@media (hover: hover) and (pointer: fine) {
  .ui-enlace:hover { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
  a.ui-fila:hover, button.ui-fila:hover, label.ui-fila:hover, .ui-proxima:hover, .ui-pase-quien:hover { background: var(--c-marina-suave); }
  a.ui-clase:hover { background: var(--c-marina-suave); box-shadow: -8px 0 0 var(--c-marina-suave), 8px 0 0 var(--c-marina-suave); }
  .ui-btn-ico:hover, .ui-filtro:hover .ui-filtro-in { background: var(--c-marina-medio); }
  .ui-dia:hover .ui-dia-num { background: var(--c-marina-suave); }
  .ui-dia[aria-pressed="true"]:hover .ui-dia-num { background: var(--c-turquesa-pulsado); }
  .ui-dias--form .ui-dia[aria-pressed="true"]:hover .ui-dia-num { background: var(--c-turquesa-pulsado); }
  .ui-barra-item:hover { color: var(--c-marina); }
}
.ui-barra-item:active { color: var(--c-marina); }
/* El botón que sigue a la tarjeta protagonista (Reservar clase) */
.ui-tarjeta + .ui-btn { margin-top: 16px; }
/* Segmentado sobre foto (idioma en la entrada): carril tinta translúcido, segmento turquesa SIN anillo
   (turquesa contra el carril: 3,65:1, medido en la captura) */
.ui-seg--foto { background: rgb(var(--c-tinta-rgb) / .6); } /* F16: .45 daba 3,2-3,9:1 sobre el cielo; .6 llega a 5:1 (pendientes entrada 20) */
.ui-seg--foto button, .ui-seg--foto label span { color: var(--c-hueso); }
.ui-seg--foto button[aria-pressed="true"], .ui-seg--foto label input:checked + span { background: var(--c-turquesa); color: var(--c-tinta); box-shadow: none; }
/* Firma con el dedo (casal, consentimientos): lienzo hueso con borde discontinuo, línea guía y «Borrar» */
.ui-firma { position: relative; height: 160px; border: 1px dashed var(--c-hormigon); border-radius: var(--r-control); background: var(--c-hueso); touch-action: none; }
.ui-firma canvas { display: block; width: 100%; height: 100%; border-radius: inherit; }
.ui-firma-guia { position: absolute; left: 16px; right: 16px; bottom: 40px; height: 1px; background: var(--c-regla-fuerte); pointer-events: none; }
.ui-firma-ph { position: absolute; left: 16px; bottom: 14px; font-size: var(--fs-13); color: var(--c-texto-2); pointer-events: none; }
.ui-firma > .ui-enlace { position: absolute; top: 0; right: 12px; }

/* =====================================================================
   19. REMATES DE LA REVISIÓN CRÍTICA (23-sep-2026)
   Piezas pequeñas que pedían varias pantallas y el sistema no dibujaba.
   Mismo lenguaje: neutros, marina, reglas finas, 44 px de toque.
   Las piezas grandes de la fundación (buscador, navdia, medios, QR…) se
   especifican en DESIGN.md › Components › Piezas compartidas (F3b).
   ===================================================================== */

/* 19.1 Cabecera de día fija (agenda del equipo, Mis clases, facturas por mes).
   Se pega bajo la zona segura; el ::before pinta de arena la franja del notch
   para que la lista no asome por detrás al desplazar. */
.ui-lista-cab--fija { position: sticky; top: var(--zona-arriba); z-index: var(--z-cabecera);
  margin-inline: calc(-1 * var(--margen)); padding: 10px var(--margen) 6px; background: var(--c-fondo); }
.ui-lista-cab--fija::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: var(--zona-arriba); background: var(--c-fondo); }
/* En un paso, se pega justo debajo de la cabecera de paso (zona segura + 8 + 44), sin tapar el volver */
.ui-cab-paso ~ .ui-lista-cab--fija, .ui-cab-paso ~ * .ui-lista-cab--fija { top: calc(var(--zona-arriba) + 52px); }
.ui-cab-paso ~ .ui-lista-cab--fija::before, .ui-cab-paso ~ * .ui-lista-cab--fija::before { display: none; }
.ui-hoja .ui-lista-cab--fija { top: 0; background: var(--c-superficie); }
.ui-hoja .ui-lista-cab--fija::before { display: none; }

/* 19.2 Aviso en línea que se puede cerrar (instalar la app, activar avisos) */
.ui-aviso--cerrable { grid-template-columns: 20px minmax(0, 1fr) var(--toque); }
.ui-aviso--cerrable > .ui-btn-ico { margin: -12px -14px -12px -8px; color: var(--c-texto-2); }
.ui-aviso--cerrable > .ui-btn-ico .ui-ico { width: 20px; height: 20px; }

/* 19.3 Acordeón con botón (fichas que el JS abre con una clase: .acc.abierto,
   acc-head). Mismo aspecto que el <details>; el botón lleva aria-expanded */
.ui-acordeon-cab { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%;
  min-height: var(--fila-lista); padding: 10px 0; font-weight: var(--p-fuerte); text-align: left; }
.ui-acordeon-cab .ui-ico { width: 16px; height: 16px; color: var(--c-chevron); stroke-width: 2.25; transition: transform var(--dur-estado) var(--ease-out); }
.ui-acordeon-cab[aria-expanded="true"] .ui-ico { transform: rotate(180deg); }

/* 19.4 Fila con casilla (pagos a monitores, promocionar, niveles): la casilla
   ocupa el sitio del icono; la fila entera es la etiqueta (<label>) */
.ui-fila--casilla { grid-template-columns: 24px minmax(0, 1fr) auto; cursor: pointer; }
.ui-fila--casilla + .ui-fila::before { left: 54px; }

/* 19.5 Campos: sufijo (€), importe grande, cifras, compacto (tablas), fecha */
.ui-campo-sufijo { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); color: var(--c-texto-2); pointer-events: none; }
.ui-campo-ctrl--sufijo { padding-right: 40px; }
.ui-campo-ctrl--num { text-align: right; font-variant-numeric: tabular-nums; }
/* Importe grande: SOLO si es la cifra de esa pantalla u hoja (recargar, enlace de pago). Cuenta como el Clash de la cifra */
.ui-campo-ctrl--importe { min-height: 64px; padding-right: 42px; text-align: right; font: var(--p-display) var(--fs-28)/1.1 var(--f-display); font-variant-numeric: tabular-nums; }
.ui-campo-ctrl--importe + .ui-campo-sufijo { font: var(--p-display) var(--fs-28)/1 var(--f-display); color: var(--c-tinta); }
.ui-campo-ctrl--compacto { min-height: var(--control-c); padding: 0 10px; font-size: max(16px, var(--fs-15)); }
.ui-campo-ctrl::-webkit-date-and-time-value { text-align: left; }

/* 19.6 Rango (#nivel y #nivel-surfskate de Preferencias: siguen siendo input range 0-5,
   el valor se escribe al lado en palabras o «3 de 5»). Pomo marina: es lo que se toca */
.ui-rango { -webkit-appearance: none; appearance: none; display: block; width: 100%; height: var(--toque); margin: 0; background: none; cursor: pointer; }
.ui-rango::-webkit-slider-runnable-track { height: 4px; border-radius: var(--r-pastilla); background: var(--c-arena-2); }
.ui-rango::-moz-range-track { height: 4px; border-radius: var(--r-pastilla); background: var(--c-arena-2); }
.ui-rango::-moz-range-progress { height: 4px; border-radius: var(--r-pastilla); background: var(--c-marina); }
.ui-rango::-webkit-slider-thumb { -webkit-appearance: none; width: 24px; height: 24px; margin-top: -10px; border: 0; border-radius: var(--r-redondo);
  background: var(--c-marina); box-shadow: 0 0 0 3px var(--c-hueso), var(--sombra-pomo); }
.ui-rango::-moz-range-thumb { width: 24px; height: 24px; border: 0; border-radius: var(--r-redondo); background: var(--c-marina); box-shadow: 0 0 0 3px var(--c-hueso), var(--sombra-pomo); }
.ui-rango:focus-visible { outline: none; }
.ui-rango:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--c-foco); outline-offset: 3px; }
.ui-rango:focus-visible::-moz-range-thumb { outline: 2px solid var(--c-foco); outline-offset: 3px; }

/* 19.7 Tira de días dentro de un formulario (crear clases en lote: varios días
   elegidos a la vez con aria-pressed). Varias filas de 7, sin la regla de abajo */
.ui-dias--form { margin: 0 -6px; padding-bottom: 0; border-bottom: 0; row-gap: 4px; }
/* Aquí se eligen VARIOS días (casillas, no la tira de navegar): la misma receta que el día de la tira
   (turquesa, número tinta, anillo hondo de 1,5 px) y un día sin nada no se apaga */
.ui-dias--form .ui-dia[aria-pressed="true"] .ui-dia-num { background: var(--c-turquesa); color: var(--c-tinta); box-shadow: inset 0 0 0 1.5px var(--c-turquesa-h); }
.ui-dias--form .ui-dia[aria-pressed="true"]:active .ui-dia-num { background: var(--c-turquesa-pulsado); }

/* =====================================================================
   20. PIEZAS COMPARTIDAS (F3b, 23-sep-2026) · DESIGN.md › Piezas compartidas
   Construidas con los valores cerrados de DESIGN.md: hueso sobre arena, marina
   para lo que se toca, 44 px de zona, sin sombra salvo lo que flota, ningún
   color nuevo. Marco de cada una en piezas.html (?ver=p-<pieza>).
   ===================================================================== */

/* 20.1 Buscador: 44 px (52 en formulario), lupa a 14 px, texto a 44, borrar solo con texto.
   El <input> lleva placeholder SIEMPRE (el botón borrar se esconde con :placeholder-shown).
   <div class="ui-buscador"><div class="ui-buscador-caja">
     <svg class="ui-ico">#i-buscar</svg>
     <input class="ui-buscador-ctrl" type="search" enterkeyhint="search" autocomplete="off" placeholder="…" aria-label="…">
     <button class="ui-btn-ico ui-buscador-borrar" type="button" aria-label="Borrar">#i-cerrar</button>
   </div><p class="ui-buscador-recuento" aria-live="polite">12 alumnos</p></div> */
.ui-buscador { display: block; }
.ui-buscador-caja { position: relative; }
.ui-buscador-caja > .ui-ico { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--c-texto-2); pointer-events: none; }
.ui-buscador-ctrl { display: block; width: 100%; height: 44px; margin: 0; padding: 0 44px 0 44px;
  border-radius: var(--r-control); background: var(--c-hueso); border: 1px solid var(--c-borde-campo); color: var(--c-tinta);
  font: var(--p-texto) max(16px, var(--fs-17))/1.3 var(--f-cuerpo); -webkit-appearance: none; appearance: none;
  transition: border-color var(--dur-estado) var(--ease-out), box-shadow var(--dur-estado) var(--ease-out); }
.ui-buscador--form .ui-buscador-ctrl { height: var(--control); }
.ui-buscador-ctrl::placeholder { color: var(--c-hormigon); opacity: 1; }
.ui-buscador-ctrl:focus { outline: none; border-color: var(--c-foco); box-shadow: 0 0 0 3px var(--c-turquesa-halo); }
.ui-buscador-ctrl::-webkit-search-cancel-button, .ui-buscador-ctrl::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
.ui-buscador-borrar { position: absolute; right: 0; top: 50%; transform: translateY(-50%); color: var(--c-texto-2); }
.ui-buscador-borrar .ui-ico { width: 20px; height: 20px; }
.ui-buscador-ctrl:placeholder-shown ~ .ui-buscador-borrar, .ui-buscador-borrar[hidden] { display: none; }
.ui-buscador-recuento { margin: 6px 2px 0; font-size: var(--fs-13); line-height: 1.4; color: var(--c-texto-2); }
.ui-buscador-recuento:empty { display: none; }

/* 20.2 Sugerencias bajo el buscador (role="listbox"): máximo 6 (lo corta el JS; sin scroll propio).
   La activa (flechas) en marina suave: aria-selected="true" o .ui-fila--activa.
   «Sin resultados» (.ui-sugerencias-nada) ≠ «No hemos podido buscar · Reintentar» (.ui-sugerencias-error). */
.ui-sugerencias { margin-top: 6px; }
.ui-sugerencias [role="option"] { cursor: pointer; }
.ui-sugerencias [role="option"] .ui-fila-tit, .ui-sugerencias [role="option"] .ui-fila-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-sugerencias [role="option"] > span { min-width: 0; }
.ui-sugerencias [role="option"][aria-selected="true"], .ui-sugerencias .ui-fila--activa, .ui-sugerencias [role="option"]:active { background: var(--c-marina-suave); }
.ui-sugerencias-nada, .ui-sugerencias-error { display: flex; align-items: center; flex-wrap: wrap; column-gap: 8px; min-height: 52px; margin: 0; padding: 4px 16px;
  font-size: var(--fs-15); color: var(--c-texto-2); }
.ui-sugerencias-error { color: var(--c-error); }
.ui-sugerencias-error .ui-ico { width: 16px; height: 16px; }
.ui-sugerencias-error .ui-enlace { font-size: var(--fs-15); }

/* 20.3 Tira de días que desliza (más de 7 días): sigue siendo UNA fila; 7 por pantalla,
   anclaje cada 7 días. Al entrar, el JS lleva el día elegido a la vista:
   dia.scrollIntoView({ block: 'nearest', inline: 'nearest' }) (o scrollLeft de la semana). */
.ui-dias--desliza { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: calc(100% / 7);
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.ui-dias--desliza::-webkit-scrollbar { display: none; }
.ui-dias--desliza > .ui-dia:nth-child(7n + 1) { scroll-snap-align: start; }

/* 20.4 Fila de aviso (inicio del alumno y del monitor), dentro de un .ui-grupo.
   Campana neutra (nunca por tipo); no leída = título 700 + punto marina de 8 delante.
   El fondo de borrar solo se ve durante el gesto (el contenido lo tapa en reposo).
   Contrato: la fila sigue llevando .noti-item; .ui-noti-fondo = .swipe-bg; .ui-noti-cont = .swipe-content;
   no leída = .ui-noti--no-leida o la .no-leida de siempre; saliendo = .ui-noti--sale o .fade-out.
   <div class="ui-noti noti-item no-leida"><div class="ui-noti-fondo swipe-bg">#i-papelera</div>
     <div class="ui-noti-cont swipe-content">#i-campana<div class="ui-noti-txt">
       <div class="ui-noti-cab"><span class="ui-noti-tit">…</span><time class="ui-noti-hora">hace 3 min</time></div>
       <p class="ui-noti-cuerpo">…</p></div></div></div> */
.ui-noti { position: relative; overflow: hidden; }
.ui-noti + .ui-noti::before { content: ""; position: absolute; z-index: 2; top: 0; left: 50px; right: 0; height: 1px; background: var(--c-regla); }
.ui-noti-fondo { position: absolute; inset: 0; display: flex; align-items: center; justify-content: flex-end; padding: 0 20px;
  background: var(--c-error-fondo); color: var(--c-error); }
.ui-noti-fondo .ui-ico { width: 24px; height: 24px; }
.ui-noti-cont { position: relative; z-index: 1; display: grid; grid-template-columns: 20px minmax(0, 1fr); column-gap: 14px; align-items: start;
  padding: 12px 16px; background: var(--c-superficie); touch-action: pan-y; }
.ui-noti-cont > .ui-ico { margin-top: 1px; color: var(--c-texto-2); }
.ui-noti-cab { display: flex; align-items: baseline; gap: 8px; }
.ui-noti-tit { flex: 1; min-width: 0; font-size: var(--fs-17); font-weight: var(--p-texto); color: var(--c-texto); overflow-wrap: anywhere; }
.ui-noti-hora { flex: none; font-size: var(--fs-13); color: var(--c-texto-2); white-space: nowrap; }
.ui-noti-cuerpo { margin: 2px 0 0; font-size: var(--fs-15); line-height: 1.4; color: var(--c-texto-2);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.ui-noti--no-leida .ui-noti-tit, .ui-noti.no-leida .ui-noti-tit { font-weight: var(--p-fuerte); }
.ui-noti--no-leida .ui-noti-tit::before, .ui-noti.no-leida .ui-noti-tit::before { content: ""; display: inline-block; width: 8px; height: 8px; margin: 0 8px 1px 0;
  border-radius: var(--r-redondo); background: var(--c-marina); vertical-align: middle; }
.ui-noti--sale, .ui-noti.fade-out { opacity: 0; transform: translateX(-30px); transition: opacity var(--dur-hoja) var(--ease-out), transform var(--dur-hoja) var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .ui-noti--sale, .ui-noti.fade-out { transform: none; } }

/* 20.5 QR a pantalla entera, en HUESO (nunca blanco: el lector lo lee igual). El brillo no se toca.
   <div class="ui-qr" id="qr-full" role="dialog" aria-modal="true" aria-label="…">
     <button class="ui-btn-ico ui-qr-cerrar" aria-label="Cerrar">#i-cerrar</button>
     <div class="ui-qr-lienzo" id="qr-lienzo">canvas|img|svg</div>
     <p class="ui-cifra ui-qr-importe">30 €</p><p class="ui-qr-concepto">Surf · hoy 9:30</p></div> */
.ui-qr { position: fixed; inset: 0; z-index: var(--z-dialogo); display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: calc(var(--zona-arriba) + 56px) var(--margen) calc(var(--zona-abajo) + 24px); background: var(--c-hueso); color: var(--c-texto);
  text-align: center; overflow-y: auto; animation: ui-funde var(--dur-hoja) var(--ease-out); }
.ui-qr[hidden] { display: none; }
.ui-qr-cerrar { position: absolute; top: calc(var(--zona-arriba) + 8px); right: calc(var(--margen) - 10px); color: var(--c-texto-2); }
.ui-qr-lienzo { width: min(280px, 100%); aspect-ratio: 1; margin-bottom: 16px; }
.ui-qr-lienzo > canvas, .ui-qr-lienzo > img, .ui-qr-lienzo > svg { display: block; width: 100%; height: 100%; image-rendering: pixelated; }
.ui-qr-importe { margin: 0; }
.ui-qr-concepto { margin: 0; font-size: var(--fs-17); color: var(--c-texto); }

/* 20.6 Navegador de día (o de mes): anterior · fecha · siguiente; «Hoy» solo si no es hoy. No es fila elegible.
   <div class="ui-navdia"><button class="ui-btn-ico" id="dn-prev" aria-label="Día anterior">#i-volver</button>
     <div class="ui-navdia-centro"><span class="ui-navdia-fecha">Miércoles 23 de septiembre</span>
       <button class="ui-enlace ui-navdia-hoy" type="button">Hoy</button></div>
     <button class="ui-btn-ico" id="dn-next" aria-label="Día siguiente">#i-flecha</button></div> */
.ui-navdia { display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px; align-items: center; min-height: 44px; margin: 0 -10px; }
.ui-navdia-centro { display: flex; align-items: center; justify-content: center; gap: 10px; min-width: 0; }
.ui-navdia-fecha { min-width: 0; font-size: var(--fs-17); font-weight: var(--p-fuerte); color: var(--c-tinta); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-navdia-hoy { flex: none; font-size: var(--fs-15); }
.ui-navdia-hoy[hidden] { display: none; }
.ui-navdia .ui-btn-ico:disabled { color: var(--c-hormigon); }

/* 20.7 Copiable: el valor en una línea (completo en title) + «Copiar» (pasa a «Copiado» 2 s) + compartir.
   <div class="ui-fila ui-fila--sin-icono ui-copiable"><span class="ui-copiable-valor" title="…">…</span>
     <span class="ui-copiable-acciones"><button class="ui-enlace" type="button">Copiar</button>
       <button class="ui-btn-ico" type="button" aria-label="Compartir">#i-compartir</button></span></div> */
.ui-copiable { padding-right: 6px; column-gap: 8px; }
.ui-copiable-valor { min-width: 0; font-size: var(--fs-15); color: var(--c-texto-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-copiable-acciones { display: inline-flex; align-items: center; gap: 2px; flex: none; }
.ui-copiable-acciones .ui-enlace { padding: 0 6px; }

/* 20.8 Progreso: barra de 4 px en arena 2 con relleno marina, sin rayas; texto de fase u ocupación en 13.
   Nativo: <progress class="ui-progreso-barra" max="100" value="42"> (ya es progressbar), o bien
   <div class="ui-progreso-barra" role="progressbar" aria-valuenow="42" aria-valuemin="0" aria-valuemax="100">
     <span class="ui-progreso-relleno"></span></div> con el JS poniendo style.setProperty('--progreso', '42%').
   <div class="ui-progreso"><p class="ui-progreso-txt">42 de 49 tablas</p><…barra…></div> */
.ui-progreso { display: grid; gap: 6px; }
.ui-progreso-txt { margin: 0; font-size: var(--fs-13); line-height: 1.4; color: var(--c-texto-2); }
.ui-progreso-barra { display: block; width: 100%; height: 4px; margin: 0; border: 0; border-radius: var(--r-pastilla); overflow: hidden;
  background: var(--c-arena-2); color: var(--c-marina); -webkit-appearance: none; appearance: none; }
.ui-progreso-relleno { display: block; height: 100%; width: var(--progreso, 0%); border-radius: inherit; background: var(--c-marina);
  transition: width var(--dur-estado) var(--ease-out); }
progress.ui-progreso-barra::-webkit-progress-bar { background: var(--c-arena-2); border-radius: var(--r-pastilla); }
progress.ui-progreso-barra::-webkit-progress-value { background: var(--c-marina); border-radius: var(--r-pastilla); }
progress.ui-progreso-barra::-moz-progress-bar { background: var(--c-marina); border-radius: var(--r-pastilla); }

/* 20.9 Subida: zona de 120 px, hueso, borde discontinuo hormigón de 1 px; icono marina y dos acciones
   compactas (principal «Hacer foto», secundaria «Elegir archivo»). Con archivo: miniatura de 72,
   nombre y tamaño, «Quitar» (.ui-enlace--peligro). El error va DEBAJO, en un .ui-aviso--error.
   Mientras se arrastra un archivo encima: .ui-subida--encima.
   <div class="ui-subida">#i-camara<div class="ui-subida-acciones">
     <label class="ui-btn ui-btn--principal ui-btn--compacto">Hacer foto<input type="file" accept="image/*" capture="environment" hidden></label>
     <label class="ui-btn ui-btn--secundario ui-btn--compacto">Elegir archivo<input type="file" hidden></label></div></div>
   <div class="ui-subida ui-subida--archivo"><img class="ui-subida-mini" id="preview…" alt="">
     <span class="ui-subida-info"><span class="ui-subida-nombre">factura.jpg</span><span class="ui-subida-tam">1,2 MB</span></span>
     <button class="ui-enlace ui-enlace--peligro" type="button">Quitar</button></div> */
.ui-subida { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; min-height: 120px; padding: 16px;
  background: var(--c-hueso); border: 1px dashed var(--c-hormigon); border-radius: var(--r-control); text-align: center;
  transition: background-color var(--dur-estado) var(--ease-out), border-color var(--dur-estado) var(--ease-out); }
.ui-subida > .ui-ico { width: 24px; height: 24px; color: var(--c-marina); }
.ui-subida-acciones { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.ui-subida--encima { border-color: var(--c-marina); background: var(--c-marina-suave); }
.ui-subida--archivo { flex-direction: row; justify-content: flex-start; gap: 14px; min-height: 0; padding: 12px 8px 12px 12px; border-style: solid; border-color: var(--c-regla); text-align: left; }
.ui-subida-mini { width: 72px; height: 72px; flex: none; border-radius: var(--r-carril); object-fit: cover; background: var(--c-arena-2); }
.ui-subida-info { flex: 1; min-width: 0; }
.ui-subida-nombre { display: block; font-size: var(--fs-17); color: var(--c-texto); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-subida-tam { display: block; margin-top: 2px; font-size: var(--fs-13); color: var(--c-texto-2); }
.ui-subida--archivo .ui-enlace { flex: none; padding: 0 8px; }

/* 20.10 Medios: rejilla de miniaturas cuadradas (3 columnas; 4 desde 600 px; 6 en escritorio), 2 px de hueco.
   Solo la miniatura (thumb) en la rejilla, con loading="lazy". Vídeo: .ui-medio--video + icono i-video (.ui-medio-tipo).
   Elegida: aria-pressed="true" (o .ui-medio--elegida): anillo interior turquesa hondo de 3 px y check tinta en círculo turquesa de 22.
   <div class="ui-medios"><button class="ui-medio gf-celda" type="button" aria-pressed="false">
     <img src="thumb" loading="lazy" alt=""><svg class="ui-ico ui-medio-tipo">#i-video</svg>
     <svg class="ui-ico ui-medio-check">#i-check</svg></button></div> */
.ui-medios { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2px; }
@media (min-width: 600px) { .ui-medios { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1024px) and (pointer: fine) { .ui-medios { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.ui-medio { position: relative; display: block; aspect-ratio: 1; width: 100%; margin: 0; padding: 0; border: 0; overflow: hidden;
  background: var(--c-arena-2); cursor: pointer; }
.ui-medio > img, .ui-medio > video { display: block; width: 100%; height: 100%; object-fit: cover; }
.ui-medio--video::after { content: ""; position: absolute; inset: 50% 0 0; pointer-events: none;
  background: linear-gradient(to top, rgb(var(--c-tinta-rgb) / .4), rgb(var(--c-tinta-rgb) / 0)); }
.ui-medio-tipo { position: absolute; z-index: 1; left: 6px; bottom: 6px; width: 16px; height: 16px; color: var(--c-hueso); }
.ui-medio-check { position: absolute; z-index: 2; top: 6px; right: 6px; width: 22px; height: 22px; padding: 4px; display: none;
  border-radius: var(--r-redondo); background: var(--c-turquesa); color: var(--c-tinta); stroke-width: 2.25; box-shadow: inset 0 0 0 1.5px var(--c-turquesa-h); }
.ui-medio[aria-pressed="true"]::before, .ui-medio--elegida::before { content: ""; position: absolute; z-index: 1; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 3px var(--c-turquesa-h); }
.ui-medio[aria-pressed="true"] .ui-medio-check, .ui-medio--elegida .ui-medio-check { display: block; }
.ui-medio:focus-visible { outline: 2px solid var(--c-foco); outline-offset: -2px; }

/* 20.11 Visor de medios: la ÚNICA superficie oscura a pantalla completa. Fondo tinta, sin velo ni desenfoque.
   <div class="ui-visor" role="dialog" aria-modal="true" aria-label="…">
     <button class="ui-btn-ico ui-visor-cerrar" aria-label="Cerrar">#i-cerrar</button>
     <button class="ui-btn-ico ui-visor-ant" aria-label="Anterior">#i-volver</button>
     <figure class="ui-visor-medio"><img|video></figure>
     <button class="ui-btn-ico ui-visor-sig" aria-label="Siguiente">#i-flecha</button>
     <p class="ui-visor-cuenta">3 de 24</p></div> */
.ui-visor { position: fixed; inset: 0; z-index: var(--z-dialogo); background: var(--c-tinta); color: var(--c-hueso);
  animation: ui-funde var(--dur-hoja) var(--ease-out); }
.ui-visor[hidden] { display: none; }
.ui-visor-medio { position: absolute; inset: calc(var(--zona-arriba) + 56px) 0 calc(var(--zona-abajo) + 44px); margin: 0;
  display: flex; align-items: center; justify-content: center; }
.ui-visor-medio > img, .ui-visor-medio > video { display: block; max-width: 100%; max-height: 100%; object-fit: contain; }
.ui-visor .ui-btn-ico { position: absolute; z-index: 1; color: var(--c-hueso); }
.ui-visor .ui-btn-ico:active { background: rgb(var(--c-hueso-rgb) / .14); }
/* anterior y siguiente caen encima de la foto: un círculo de tinta al 40 % para que se lean sobre un cielo claro (no es un velo) */
.ui-visor-ant, .ui-visor-sig { background: rgb(var(--c-tinta-rgb) / .4); }
.ui-visor-ant:active, .ui-visor-sig:active { background: rgb(var(--c-tinta-rgb) / .6); }
.ui-visor .ui-btn-ico:focus-visible { outline: 2px solid var(--c-hueso); outline-offset: 2px; }
.ui-visor .ui-btn-ico[disabled]:not([aria-busy="true"]), .ui-visor .ui-btn-ico[hidden] { display: none; } /* F16: el ocupado (descargando) se queda a la vista */
.ui-visor-cerrar { top: calc(var(--zona-arriba) + 8px); right: 6px; }
.ui-visor-ant { left: 6px; top: 50%; transform: translateY(-50%); }
.ui-visor-sig { right: 6px; top: 50%; transform: translateY(-50%); }
.ui-visor-cuenta { position: absolute; left: 0; right: 0; bottom: calc(var(--zona-abajo) + 14px); margin: 0; text-align: center;
  font-size: var(--fs-13); color: var(--c-texto-sobre-marina-2); }

/* 20.12 Mapa (Leaflet de cdnjs con SRI): contenedor de 240 px (320 en escritorio), esquina 14, sin sombra ni borde.
   Controles de 44 en hueso con icono marina y regla de 1 px entre ellos; marcador .ui-punto de 12 en marina con
   anillo hueso de 2 px (divIcon con className "ui-punto"); atribución en 12 tinta 2 sobre hueso al 85 %.
   Las teselas no se filtran. Nada de backdrop-filter en los controles. */
.ui-mapa { position: relative; height: 240px; border-radius: var(--r-control); overflow: hidden; background: var(--c-arena-2); isolation: isolate; }
@media (min-width: 1024px) and (pointer: fine) { .ui-mapa { height: 320px; } }
.ui-mapa .leaflet-control.leaflet-bar { border: 0; border-radius: var(--r-control); box-shadow: none; overflow: hidden; }
.ui-mapa .leaflet-control.leaflet-bar a { display: block; text-align: center; text-decoration: none; width: 44px; height: 44px; line-height: 44px; border: 0; border-radius: 0; background: var(--c-hueso); color: var(--c-marina);
  font: var(--p-texto) 22px/44px var(--f-cuerpo); }
.ui-mapa .leaflet-control.leaflet-bar a + a { border-top: 1px solid var(--c-regla); }
.ui-mapa .leaflet-control.leaflet-bar a:active { background: var(--c-marina-suave); }
.ui-mapa .leaflet-control.leaflet-bar a.leaflet-disabled { color: var(--c-hormigon); background: var(--c-hueso); }
.ui-mapa .leaflet-control-attribution { background: rgb(var(--c-hueso-rgb) / .85); color: var(--c-texto-2); font-size: var(--fs-12); }
.ui-mapa .leaflet-control-attribution a { color: var(--c-marina); }
.ui-mapa .ui-punto { width: 12px; height: 12px; --punto: var(--c-marina); border: 0; box-shadow: 0 0 0 2px var(--c-hueso); }

/* 20.13 Punto de estado «ok» del push (DESIGN.md › The One Dot Rule: la segunda excepción cerrada), en turquesa hondo */
.ui-punto--ok { --punto: var(--c-turquesa-h); }
/* F13: los otros dos estados del push (utils/push-diagnostico.js pone ui-punto--ok|--aviso|--error) */
.ui-punto--aviso { --punto: var(--c-aviso); }
.ui-punto--error { --punto: var(--c-error); }

/* =====================================================================
   21. F9 · lo que necesitan utils/hoja.js, utils/dialogo.js y utils/estados.js
   ===================================================================== */
/* La hoja vive en el HTML con [hidden] hasta que abrirHoja la abre (la alta y la lateral son flex) */
.ui-hoja[hidden] { display: none; }
/* El foco inicial va a la hoja o al diálogo entero (el lector lee su título): sin anillo en la caja */
.ui-hoja:focus, .ui-hoja:focus-visible, .ui-dialogo:focus, .ui-dialogo:focus-visible { outline: none; }
/* El arrastre de deslizar-para-cerrar.js no pelea con la animación de entrada */
.ui-hoja.deslizando { animation: none; }
/* Movimiento reducido: el panel lateral de escritorio también funde (va detrás del bloque de escritorio) */
@media (prefers-reduced-motion: reduce) { .ui-hoja--lateral { animation-name: ui-funde; } }
/* Esqueleto: el envoltorio que lleva aria-busy; fila de grupo (icono · texto · cifra) y tarjetas apiladas */
.ui-esq-grupo { display: block; }
.ui-esq--ico { width: 24px; height: 24px; border-radius: var(--r-seg); }
.ui-esq--cifra { width: 48px; }
.ui-esq--bloque + .ui-esq--bloque { margin-top: 12px; }

/* =====================================================================
   22. F10 · Asistente (utils/chat-asistente.js en páginas v2): hoja alta,
   burbujas hueso sobre arena, la tuya en marina; campo con etiqueta y enviar con i-flecha
   ===================================================================== */
.ui-asis-msgs { display: flex; flex-direction: column; gap: 8px; padding-top: 16px; padding-bottom: 16px; background: var(--c-arena); }
.ui-asis-b { max-width: 88%; padding: 10px 14px; border-radius: var(--r-control); font-size: var(--fs-15); line-height: 1.45;
  white-space: pre-wrap; overflow-wrap: anywhere; }
.ui-asis-b--assistant { align-self: flex-start; background: var(--c-hueso); color: var(--c-tinta); }
.ui-asis-b--user { align-self: flex-end; background: var(--c-marina); color: var(--c-hueso); }
.ui-asis-b--user .ui-enlace { color: inherit; text-decoration: underline; }
.ui-asis-b--error { background: var(--c-error-fondo); }
.ui-asis-b--escribiendo { color: var(--c-texto-2); animation: ui-asis-late 1.2s var(--ease-in-out) infinite; }
@keyframes ui-asis-late { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .ui-asis-b--escribiendo { animation: none; } }
.ui-asis-form { display: grid; grid-template-columns: minmax(0, 1fr) var(--toque); gap: 8px; align-items: center; }
.ui-asis-enviar { color: var(--c-marina); }

/* F13: la banda nace oculta (utils/offline.js la enseña sin red) */
.ui-banda[hidden] { display: none; }

/* =====================================================================
   23. F15 · Remates que pidieron las zonas del lote 1
   (.claude/banco/pendientes-fundacion/*.md). Las zonas conservan su parche
   local, que pinta lo mismo, hasta que se retire en F16.
   ===================================================================== */
/* [hidden] gana al display de los componentes (.ui-fila, .ui-aviso, .ui-btn, .ui-grupo, .ui-total…).
   Doble atributo = (0,3,0): gana a las reglas de componente; un display en línea (style) o una
   regla con id siguen mandando, como antes. Sin !important a propósito. */
.v2 [hidden][hidden] { display: none; }
/* Fila que es un <button> (abre algo con JS): sin el aspecto del navegador */
button.ui-fila { border: 0; background: none; font: inherit; }
/* Fila de clase que es un <button> (Agenda): ancho entero y texto al principio */
button.ui-clase { width: 100%; text-align: start; }
@media (hover: hover) and (pointer: fine) {
  button.ui-clase:hover { background: var(--c-marina-suave); box-shadow: -8px 0 0 var(--c-marina-suave), 8px 0 0 var(--c-marina-suave); }
}
.ui-clase-cuantos { font-variant-numeric: tabular-nums; }
/* Pase de lista: el presente en 700 también sin :has() (el JS pone la clase) */
.ui-pase--presente .ui-pase-nom { font-weight: var(--p-fuerte); }
/* Opción que no es un radio nativo (role="radio" con tabindex): foco y deshabilitada */
.ui-opcion[role="radio"]:focus-visible { outline: 2px solid var(--c-foco); outline-offset: -2px; }
.ui-opcion[aria-disabled="true"] { cursor: default; }
.ui-opcion[aria-disabled="true"] > .ui-ico { color: var(--c-texto-2); }
/* Campos a la par: la segunda columna no baja 16 px y un input date puede encoger */
.ui-campo-fila > .ui-campo { margin-top: 0; min-width: 0; }
.ui-campo-fila .ui-campo-ctrl { min-width: 0; }
/* Enlace dentro de un aviso («Activar», «Cómo instalarla»): mide 36 px; el toque llega a 44 */
.ui-aviso .ui-enlace { position: relative; }
.ui-aviso .ui-enlace::after { content: ""; position: absolute; inset: -4px -8px; }
/* Diálogo con texto largo en una pantalla baja: scroll propio */
.ui-dialogo { max-height: calc(100vh - 48px); max-height: calc(100dvh - 48px); overflow-y: auto; overscroll-behavior: contain; }
/* Cabecera de hoja con título largo: el bloque del título encoge, la X no (flex: none en .ui-btn-ico) */
.ui-hoja-cab > :not(.ui-btn-ico) { min-width: 0; }
/* Próxima clase sin pie (paddle, o sin punto de encuentro): redonda también abajo */
.ui-proxima:last-child { border-radius: var(--r-control); }
/* Pie de la próxima clase: «Cómo llegar» no se parte y el sitio en 2+ líneas no toca la regla */
.ui-proxima-pie > .ui-enlace { flex: none; white-space: nowrap; }
.ui-proxima-sitio { padding-block: 12px; }

/* =====================================================================
   21. AJUSTES DE LAS NOTAS DE NACHO (23-sep-2026, viendo la web publicada)
   «El mismo estilo en la app»: bolita de actividad, lista agrupada en
   tarjeta, tira de días sin puntos (sección 7) y títulos más contenidos
   (--fs-titulo). DESIGN.md › Actividad: una bolita, › Lista de clases
   agrupada. Helpers en utils/tipos-clase.js (puntoTipo, puntoHTML, Kids).
   ===================================================================== */

/* 21.1 Lista de clases agrupada a lo iOS: las filas de UN día en una tarjeta hueso con esquinas
   de 14 sobre el arena; la cabecera del día, fuera. Sin sombra. Separador fino DENTRO, desde la
   hora hasta el borde derecho. La tarjeta sale 6 px por cada lado, hasta la regla de la tira.
   Solo filas .ui-clase dentro: el vacío, el error y el esqueleto van fuera de la tarjeta. */
.ui-lista--grupo { margin-inline: -6px; background: var(--c-superficie); border-radius: var(--r-control); overflow: hidden; }
.ui-lista-cab + .ui-lista--grupo { margin-top: 8px; }
.ui-lista--grupo > .ui-clase { position: relative; padding: 10px 12px 10px 14px; border-bottom: 0; }
.ui-lista--grupo > .ui-clase + .ui-clase::before { content: ""; position: absolute; top: 0; left: 14px; right: 0; height: 1px; background: var(--c-regla); }
/* Pulsado: la fila entera dentro de la tarjeta (sin la franja que se salía por los lados) */
.ui-lista--grupo > .ui-clase:active { box-shadow: none; }
.ui-lista--grupo > .ui-clase:focus-visible { outline-offset: -2px; }
@media (hover: hover) and (pointer: fine) {
  .ui-lista--grupo > a.ui-clase:hover, .ui-lista--grupo > button.ui-clase:hover { box-shadow: none; }
}
/* Por debajo de 390 px (iPhone SE y mini, Android de 360 y 320): la tarjeta devuelve relleno */
@media (max-width: 389px) {
  .ui-lista--grupo > .ui-clase { grid-template-columns: 52px minmax(0, 1fr) auto 14px; padding-inline: 10px 8px; column-gap: 10px; }
  .ui-lista--grupo > .ui-clase + .ui-clase::before { left: 10px; }
}

/* 21.2 Bolita en la fila de clase, colgada: el nombre y su nota empiezan en la misma vertical.
   <span class="ui-clase-txt">${puntoHTML(c)}<span class="ui-clase-nombre">Surf</span><span class="ui-clase-nota">…</span></span>
   (sin nota: <span class="ui-clase-txt">${puntoHTML(c)}<span class="ui-clase-nombre">Surf</span></span>).
   La bolita se centra en la primera línea del nombre. Una fila sin tipo conocido no lleva bolita
   y su texto se alinea igual (el hueco se queda). */
.ui-clase-txt { position: relative; display: block; min-width: 0; padding-left: 16px; }
.ui-clase-txt > .ui-punto, .ui-clase-txt > .ui-clase-nombre > .ui-punto { position: absolute; left: 0; margin: 0;
  top: calc(var(--fs-17) * var(--lh-ui) / 2 - 4px); }
.ui-clase-txt > .ui-clase-nombre { display: block; line-height: var(--lh-ui); }
/* El punto de «Surf confirmado» va SIEMPRE con la sangría del nombre, también por debajo de 390 px: si se
   colgara en la columna de la bolita, la de Surf (#2F9C93) y él (#7FD3C6, 1,9:1 entre sí) parecerían dos
   viñetas iguales. A 320 la nota parte en dos líneas si hace falta. */
/* La bolita en el título de una hoja de detalle («● Kids · 16:30»), alumno y equipo: 10 px, como en el
   título de la reserva, centrada en la línea. */
.ui-hoja-tit > .ui-punto { width: 10px; height: 10px; margin: 0 8px 0 1px; vertical-align: .18em; }
/* Ya empezó, completa o cancelada: la bolita se apaga con la fila (decorativa; el texto no usa opacidad) */
.ui-clase--pasada .ui-punto, .ui-clase--completa .ui-punto, .ui-clase--cancelada .ui-punto { opacity: .45; }

/* 21.3 Bolita en la hoja «Todas ▾» (y en el botón cuando hay filtro): 10 px delante de cada opción.
   <label class="ui-fila ui-fila--sin-icono ui-fila--punto ui-opcion"><input …>${puntoHTML('Surf')}<span class="ui-fila-tit">Surf</span>✓</label>
   La bolita va FUERA de .ui-fila-tit (updateTexts reescribe su texto). «Todas», en quesitos
   (ui-punto--todas); «Otras», hueca (ui-punto--otros). */
.ui-fila--punto { grid-template-columns: 10px minmax(0, 1fr) auto; column-gap: 12px; }
.ui-fila--punto > .ui-punto { width: 10px; height: 10px; }
.ui-fila--punto + .ui-fila--punto::before { left: 38px; }
.ui-punto--otros { background: none; box-shadow: inset 0 0 0 1.5px var(--c-hormigon); }
.ui-filtro-in > .ui-punto { margin-right: 2px; }

/* 21.4 La actividad en el título de la reserva: marina, con su bolita de 10 px delante.
   Sin tocar el texto: <h1 class="ui-cab-titulo ui-titulo-act ${puntoTipo(tipo)}"> (la clase
   ui-punto--* en el h1 solo pone el color de la bolita). Sin tipo conocido, sin bolita. */
.ui-titulo-act { color: var(--c-marina); }
.ui-titulo-act::before { content: ""; display: inline-block; width: 10px; height: 10px; margin: 0 10px 0 1px; border-radius: var(--r-redondo);
  background: var(--punto); vertical-align: .2em; }
.ui-titulo-act:not([class*="ui-punto--"])::before { display: none; }

/* 21.5 Subtipo con «?» (Kids: «Extraescolar · St. George» ?). El «?» es un botón de 28 px que
   toca en 44 (::after), con aria-expanded/aria-controls, FUERA de cualquier <label>; abre debajo
   un párrafo corto. Marcado y manejo: subtipoKidsHTML() + activarAyudas() de utils/tipos-clase.js */
.ui-subtipo { display: flex; flex-wrap: wrap; align-items: center; column-gap: 4px; font-size: var(--fs-15); color: var(--c-texto-2); }
.ui-que { position: relative; display: inline-grid; place-items: center; width: 28px; height: 28px; flex: none; padding: 0; border: 0;
  border-radius: var(--r-redondo); background: none; color: var(--c-marina); cursor: pointer;
  transition: background-color var(--dur-estado) var(--ease-out); }
.ui-que::after { content: ""; position: absolute; inset: -8px; }
.ui-que .ui-ico { width: 20px; height: 20px; }
.ui-que:active, .ui-que[aria-expanded="true"] { background: var(--c-marina-suave); }
.ui-que-txt { flex-basis: 100%; margin: 6px 0 10px; max-width: var(--medida); font-size: var(--fs-15); line-height: 1.5; color: var(--c-texto-2); }
@media (hover: hover) and (pointer: fine) { .ui-que:hover { background: var(--c-marina-suave); } }

/* =====================================================================
   24. F15/F16 · Remates que pidieron las zonas de los lotes 2 y 3
   (.claude/banco/pendientes-fundacion/*.md). Donde la zona tiene un parche
   local marcado PENDIENTE-FUNDACION que pinta lo mismo, se puede borrar;
   mientras siga, no molesta (mismos valores).
   ===================================================================== */
/* 24.1 Campos: compacto + sufijo (precios en filas de catálogo): el «€» no se monta encima de la cifra */
.ui-campo-ctrl--compacto.ui-campo-ctrl--sufijo { padding-right: 32px; }
/* 24.2 Ritmo de .ui-form con filas dobles: 16 px (el gap), no 32, antes y después de cada fila doble */
.ui-form > .ui-campo + .ui-campo-fila, .ui-form > .ui-campo-fila + .ui-campo, .ui-form > .ui-campo-fila + .ui-campo-fila { margin-top: 0; }
/* 24.3 Filas: título y subtítulo en una línea con «…» (emails largos en Recientes y en Alumnos) */
.ui-fila-tit--corta, .ui-fila-sub--corta { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* 24.4 Importe a la derecha de una fila (pagos de Stripe, facturas, enlaces, informes): tinta y cifras tabulares */
.ui-fila-valor--importe { color: var(--c-texto); font-variant-numeric: tabular-nums; }
/* 24.5 Estado «Pagado» en texto: turquesa hondo 700, sin el punto (reservado a lo confirmado) */
.ui-etq--ok { color: var(--c-ok); font-weight: var(--p-fuerte); }
/* 24.6 Segunda línea de fila en ámbar (motivo de un aviso) sin perder contra .ui-fila-sub */
.ui-fila-sub.ui-etq--aviso, .ui-fila-sub--aviso { color: var(--c-aviso); }
/* 24.7 Avatar pequeño (tablas de escritorio): 32 px, letra 13 */
.ui-av--peq { width: 32px; height: 32px; font-size: var(--fs-13); }
/* 24.8 Fila elegida en una lista de maestro-detalle (la tabla ya lo hace con aria-selected) */
.ui-grupo .ui-fila[aria-current="true"] { background: var(--c-marina-suave); }
/* 24.9 Fila de esqueleto dentro de un .ui-grupo: usa la rejilla de .ui-fila (icono · texto · cifra); no se toca */
.ui-esq-fila--grupo { pointer-events: none; }
/* 24.10 Lista agrupada dentro de una hoja hueso: la tarjeta pasa a arena, sin salirse del margen */
.ui-hoja .ui-lista--grupo { background: var(--c-arena); margin-inline: 0; }
/* 24.11 Navegador de día con segunda línea («Semana 31 ago — 4 sept»), bajo la fecha */
.ui-navdia-sub { margin-top: 2px; font-size: var(--fs-15); color: var(--c-texto-2); text-align: center; }
/* 24.12 Buscador deshabilitado (ficha de solo lectura): como .ui-campo-ctrl:disabled y sin «borrar» */
.ui-buscador-ctrl:disabled { background: var(--c-arena-2); border-color: transparent; color: var(--c-texto-2); }
.ui-buscador-ctrl:disabled ~ .ui-buscador-borrar { display: none; }
/* 24.13 Segmentado hecho con enlaces (idioma de la entrada: languaje.js pone .active) y con radios ARIA (ficha del mar) */
.ui-seg a { display: grid; place-items: center; min-height: 40px; padding: 0 10px; border-radius: var(--r-seg); font-size: var(--fs-15);
  color: var(--c-texto-2); text-decoration: none; }
.ui-seg a.active, .ui-seg a[aria-current="true"], .ui-seg [role="radio"][aria-checked="true"] { background: var(--c-turquesa); color: var(--c-tinta);
  font-weight: var(--p-fuerte); box-shadow: inset 0 0 0 1px var(--c-turquesa-h); }
.ui-seg--foto a { color: var(--c-hueso); }
.ui-seg--foto a.active, .ui-seg--foto a[aria-current="true"] { color: var(--c-tinta); box-shadow: none; }
/* 24.14 Segmentado que desliza (doce horas del spot): una fila con scroll, cada segmento a su ancho */
.ui-seg--desliza { display: flex; overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; }
.ui-seg--desliza::-webkit-scrollbar { display: none; }
.ui-seg--desliza > * { flex: none; padding: 0 14px; }
/* 24.15 Segmentado en rejilla (seis opciones en 3 × 2): excepción de formulario rápido de la playa; parte por espacios */
.ui-seg.ui-seg--rejilla { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-flow: row; gap: 2px; }
.ui-seg--rejilla button { line-height: 1.2; padding: 4px 6px; overflow-wrap: break-word; }
/* 24.16 Botón «?» dentro de una línea de texto: 44 px de toque que no estiran la línea */
.ui-btn-ico--en-linea { margin: -10px; }
/* 24.17 Mapa con alto propio (los SVG de la web en el mar): sin alto fijo, fondo tinta, capas encerradas */
.ui-mapa--auto { height: auto; background: var(--c-tinta); }
@media (min-width: 1024px) and (pointer: fine) { .ui-mapa--auto { height: auto; } }
/* 24.18 Lectura: listas, subtítulos, tablas y notas (privacidad, condiciones de la clase fija y de extraescolares) */
.ui-lectura ul { margin: 8px 0 0; padding-left: 22px; list-style: disc; }
.ui-lectura li + li { margin-top: 6px; }
.ui-lectura p + ul, .ui-lectura ul + p { margin-top: 12px; }
.ui-lectura h3 { margin: 20px 0 4px; font-size: var(--fs-17); font-weight: var(--p-fuerte); line-height: 1.35; }
.ui-lectura table { width: 100%; margin-top: 12px; border-collapse: collapse; font-size: var(--fs-15); line-height: 1.45; }
.ui-lectura th, .ui-lectura td { padding: 10px 12px 10px 0; text-align: left; vertical-align: top; border-bottom: 1px solid var(--c-regla); }
.ui-lectura th { font-weight: var(--p-fuerte); border-bottom-color: var(--c-regla-fuerte); }
/* 24.19 Barra de acción en escritorio: la fila no se estira a 1200 px (Guardar, Pagar, Registrar cobro) */
@media (min-width: 1024px) and (pointer: fine) {
  .ui-accion-fila, .ui-accion-nota { max-width: 640px; margin-inline: auto; }
}
/* 24.20 Hoja «Estado de tus notificaciones» (utils/push-diagnostico.js en páginas v2): comprobaciones en un
   grupo con su punto, aviso y principal debajo, dispositivos en otro grupo y la nota del buzón al pie */
.pdx-hoja .ui-grupo + .ui-aviso, .pdx-hoja .ui-grupo + .ui-btn, .pdx-hoja .ui-aviso + .ui-btn { margin-top: 16px; }
.pdx-hoja .ui-fila > .ui-punto { margin-inline: 2px; }
.pdx-hoja .pdx-disp-v2 { margin-top: 24px; }
.pdx-hoja .pdx-disp-v2 .ui-bloque-tit { margin-bottom: 8px; }
.pdx-hoja .ui-fila-valor .ui-etq { font-size: var(--fs-15); }
.pdx-hoja .pdx-quitar-v2 { margin-right: -10px; color: var(--c-texto-2); }
.pdx-pie-v2 { margin-top: 16px; font-size: var(--fs-13); line-height: 1.45; color: var(--c-texto-2); text-align: center; }
.pdx-hoja .ui-grupo > .ui-fila + .ui-fila::before { left: 42px; }

/* ---------- 25. Etiqueta de monitores (solo equipo: agenda y detalle de clase) ----------
   Nacho, 24-sep-2026: «que el monitor vea rápido en qué clase se puede apuntar» y, cerca del límite del ratio,
   «que esté como naranja: hay monitor, pero seguramente haga falta otro». La lógica vive en
   utils/estado-monitores.js (el ratio, en utils/ratio-monitores.js). Es la ÚNICA etiqueta rellena de la app:
   pide actuar a alguien que repasa 45 días de agenda, y en texto (como «Sin monitor» en ámbar) se perdía.
   Una por fila, con icono y palabra (nunca solo color) y nunca turquesa: esto es trabajo pendiente, no algo
   elegido ni bueno. Tres niveles, de más a menos urgente:
   · sin monitor: marina entero, el color de lo que se toca (hueso encima 11,6:1). La invitación a apuntarse.
   · falta otro monitor: error con hueso encima (5,9:1, la receta del botón de peligro). Ya pasa del ratio.
   · hará falta otro: ámbar sobre su fondo (6,6:1). Quedan 3 plazas o menos hasta el ratio de los que hay.
   En la fila va en su propia línea (fila 2 de la rejilla), alineada con el nombre y con el ancho de la fila (a
   390 px no cabe en la columna del nombre), DEBAJO del nombre y de su nota: no la sustituye. «Por confirmar»,
   «Surf confirmado», «Sin olas», «Cerrada»… siguen en su sitio de siempre (a 390 px no caben al lado de la
   etiqueta: ahí saltaban de sitio según la fila). Sin movimiento: nada que apagar en html.lowend ni con
   prefers-reduced-motion. */
.ui-mon { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-height: 26px; padding: 2px 10px 2px 8px;
  border-radius: var(--r-seg); font-size: var(--fs-15); font-weight: var(--p-fuerte); line-height: var(--lh-ui); }
.ui-mon > .ui-ico { width: 16px; height: 16px; stroke-width: 2; }
.ui-mon > span { min-width: 0; }
.ui-mon--sin { background: var(--c-marina); color: var(--c-texto-sobre-marina); }
.ui-mon--falta { background: var(--c-error); color: var(--c-hueso); }
.ui-mon--pronto { background: var(--c-aviso-fondo); color: var(--c-aviso); }
.ui-clase > .ui-clase-mon { grid-column: 2 / -1; grid-row: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px;
  min-width: 0; margin-top: 6px; padding-left: 16px; }
/* Por debajo de 360 px (Android de 320) no cabe junto al nombre sin partirse: la etiqueta ocupa la fila
   entera, bajo la hora */
@media (max-width: 359px) {
  .ui-clase > .ui-clase-mon { grid-column: 1 / -1; padding-left: 0; }
}
/* Detalle: la etiqueta y el porqué con las cifras, justo encima del botón principal («Doy esta clase») */
.ui-mon-det { display: grid; justify-items: start; gap: 6px; margin-bottom: 4px; }
.ui-mon-det-txt { font-size: var(--fs-15); line-height: 1.45; color: var(--c-texto-2); }
/* Tabla de escritorio: en la columna «Estado», la etiqueta y la nota. La etiqueta se lee entera (es la llamada
   a apuntarse): parte en dos líneas antes que recortarse, y la celda crece (a 1024 px la columna mide 195 px
   y «Will need another instructor», 225). */
.ui-mon-celda { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding-block: 6px; white-space: normal; }
