/* Los mismos valores que el canvas de diseño (`diseno/`), para que lo aprobado y lo construido no
   se separen. El color de marca lo pone cada tienda: entra como `--marca` desde la plantilla. */
:root {
  /* Lo que una ventanita `.detalle-cerrar` deja hasta el borde de la pantalla: su ancho máximo lo
     descuenta a los dos lados y `acciones.js` la desplaza para respetarlo. */
  --margen-panel: 16px;
  --fondo: oklch(0.97 0.006 85);
  --texto: oklch(0.24 0.012 85);
  --apagado: oklch(0.55 0.01 85);
  --borde: oklch(0.90 0.006 85);
  --borde-suave: oklch(0.955 0.004 85);
  --fondo-suave: oklch(0.96 0.015 85);
  --tarjeta: white;
  --marca: #8a3d1f;
  --marca-secundaria: #8a3d1f;
  --rojo: oklch(0.51 0.15 25);
  --rojo-fondo: oklch(0.96 0.03 25);
  --verde: oklch(0.44 0.10 155);
  --ambar: oklch(0.50 0.12 65);
  --ambar-fondo: oklch(0.96 0.05 85);
}

* { box-sizing: border-box; }
body {
  margin: 0; background: var(--fondo); color: var(--texto);
  font-family: ui-sans-serif, system-ui, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}
.num { font-variant-numeric: tabular-nums; }

/* Texto sólo para lectores de pantalla (y para el enlace de salto, que se ve al recibir el foco). */
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.sr-only:focus, .sr-only:focus-visible {
  position: fixed; top: 8px; left: 8px; z-index: 100; width: auto; height: auto; margin: 0;
  padding: 10px 16px; overflow: visible; clip: auto; clip-path: none; white-space: normal;
  background: var(--tarjeta); color: var(--texto); border-radius: 8px; font-weight: 600;
  box-shadow: 0 6px 20px rgba(0,0,0,0.25);
}

/* Barra superior */
.barra {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 10px 20px;
  padding: 14px 20px; background: var(--marca); color: white;
}
.barra a { color: rgba(255,255,255,0.9); text-decoration: none; }
.barra a:hover { color: white; }
.marca { display: flex; align-items: center; gap: 12px; font-size: 17px; font-weight: 600; }
.iniciales {
  width: 34px; height: 34px; flex-shrink: 0; border-radius: 8px; background: rgba(255,255,255,0.22);
  display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 14px;
}
.iniciales.pequena { width: 26px; height: 26px; border-radius: 50%; font-size: 11px; }
/* Doce enlaces y los menús no caben en una línea en una tableta (768–1024 px): la barra entera y la
   navegación parten en líneas en vez de desbordar. */
nav.secciones { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; font-size: 14.5px; min-width: 0; }
/* Por debajo de 1366 px, la cuenta sube junto a la marca y la navegación ocupa sola la segunda fila:
   si no, en una tableta la cuenta acababa sola en una tercera fila de cabecera. Desde ahí va todo
   en una fila, y lo que cede es el nombre de la tienda, que parte en dos líneas: con uno largo, la
   cuenta caía sola a una segunda fila incluso a 1440 px. */
@media (max-width: 1365px) {
  .barra > nav.secciones { order: 3; flex-basis: 100%; }
}
@media (min-width: 1366px) {
  .barra { flex-wrap: nowrap; }
  .barra > nav.secciones, .barra > .marca:last-child { flex-shrink: 0; }
}
nav.secciones > a[aria-current="page"], nav.secciones summary[aria-current] { font-weight: 600; color: white; }

/* Menú de la cuenta de la cabecera. `<details>` da el clic, el foco y el teclado sin una línea de
   JS; el nombre de quien ha entrado sale además en el `title` del resumen al pasar el ratón. Los
   enlaces del panel llevan su color explícito porque dentro de `.barra` heredarían el blanco. */
.menu-usuario { position: relative; }
.menu-usuario > summary { cursor: pointer; list-style: none; color: white; }
.menu-usuario > summary::-webkit-details-marker { display: none; }
.menu-usuario[open] > summary { outline: 2px solid rgba(255,255,255,0.7); outline-offset: 2px; }
.menu-usuario-panel {
  position: absolute; right: 0; top: calc(100% + 10px); z-index: 30;
  min-width: 230px; padding: 8px; display: flex; flex-direction: column; gap: 2px;
  background: var(--tarjeta); border: 1px solid var(--borde); border-radius: 11px;
  box-shadow: 0 12px 28px rgba(0,0,0,0.22);
}
.menu-usuario-nombre {
  padding: 7px 10px 9px; margin-bottom: 4px; font-size: 13px; font-weight: 600;
  color: var(--texto); border-bottom: 1px solid var(--borde-suave);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.menu-usuario-panel a {
  padding: 9px 10px; border-radius: 7px; font-size: 14px; color: var(--texto);
}
.menu-usuario-panel a:hover { background: var(--borde-suave); color: var(--texto); }
.menu-usuario-panel a[aria-current="page"] { font-weight: 600; background: var(--borde-suave); }

/* Menús «Configuración» y «Ayuda» de la barra de secciones: el mismo `<details>` y el mismo panel
   que el de la cuenta, con el resumen pintado como un enlace más de la barra. El panel se abre
   hacia la izquierda, como el de la cuenta; si la barra parte en líneas y eso lo saca de la
   pantalla, `acciones.js` lo devuelve. */
:is(.menu-configuracion, .menu-ayuda) { position: relative; }
:is(.menu-configuracion, .menu-ayuda) > summary { cursor: pointer; list-style: none; color: rgba(255,255,255,0.9); }
:is(.menu-configuracion, .menu-ayuda) > summary::-webkit-details-marker { display: none; }
:is(.menu-configuracion, .menu-ayuda) > summary:hover, :is(.menu-configuracion, .menu-ayuda)[open] > summary { color: white; }

/* Cerrar el contrato a mano (tarea 4): el motivo no hace falta verlo siempre, así que vive en un
   `<details>` —el mismo patrón que «Editar descripción», sin modal ni JS—. El panel cuelga del
   resumen en posición absoluta para no desplazar la cabecera ni la fila de la tabla. */
.detalle-cerrar { position: relative; }
.detalle-cerrar > summary { cursor: pointer; list-style: none; }
.detalle-cerrar > summary::-webkit-details-marker { display: none; }
.detalle-cerrar[open] > summary { outline: 2px solid var(--marca); outline-offset: 2px; }
.detalle-cerrar-panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 20; padding: 14px;
  /* Tan ancho como pidan sus botones (que no se parten: «Cancela / r»), sin bajar de 340 px ni salirse
     de la pantalla del móvil. */
  width: max-content; min-width: min(340px, calc(100vw - 2 * var(--margen-panel)));
  max-width: min(460px, calc(100vw - 2 * var(--margen-panel)));
  background: var(--tarjeta); border: 1px solid var(--borde); border-radius: 11px;
  box-shadow: 0 12px 28px rgba(0,0,0,0.18); text-align: left;
}
.detalle-cerrar-panel button { white-space: nowrap; }
/* Las acciones de una fila de tabla (`usuarios.html`): una sola línea, sin partir los botones. Con una
   confirmación abierta (`details.en-fila[open]`) la fila se parte en dos: los botones arriba y, debajo,
   el panel EN EL FLUJO —no colgando en absoluto sobre las filas de abajo—. `width: 0; min-width: 100%`
   hace que el panel ocupe el ancho de la celda sin contar para el ancho que la tabla le da a la
   columna: abrirlo no reorganiza las demás. */
.acciones-fila { display: flex; flex-wrap: nowrap; justify-content: flex-end; align-items: flex-start; gap: 6px; }
.acciones-fila > * { white-space: nowrap; }
.acciones-fila:has(> details.en-fila[open]) { flex-wrap: wrap; }
.acciones-fila > details.en-fila[open] { flex: 0 0 100%; order: 1; }
.acciones-fila > details.en-fila[open] > .detalle-cerrar-panel {
  position: static; width: 0; min-width: 100%; max-width: none; margin-top: 8px; box-shadow: none; white-space: normal;
}

/* Una confirmación DENTRO de otra ventanita (`_persona_supresion.html`): en el flujo del panel que la
   contiene, sin colgar encima de él. */
.detalle-cerrar.en-linea > .detalle-cerrar-panel {
  position: static; width: auto; min-width: 0; max-width: none; margin-top: 8px; box-shadow: none;
}

main { max-width: 1180px; margin: 0 auto; padding: 22px 20px 60px; }

h1 { font-size: 22px; font-weight: 600; margin: 0 0 4px; }
h1, h2, .tpv-titulo { text-wrap: balance; }
/* Lo que escribe una persona (nombres, correos, descripciones) no tiene longitud conocida: se parte
   donde haga falta en vez de desbordar la tarjeta o la celda. Las cifras no se parten. */
td, .tarjeta, h1, .tpv-linea-nombre, .migas { overflow-wrap: anywhere; }
td.der, td.num { overflow-wrap: normal; }
.subtitulo { color: var(--apagado); font-size: 14px; margin: 0 0 20px; }

.tarjeta {
  background: var(--tarjeta); border: 1px solid var(--borde); border-radius: 11px;
  padding: 18px 20px; margin-bottom: 14px;
}
.etiq {
  font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em;
  color: oklch(0.56 0.01 85); display: block; margin-bottom: 6px;
}
.rejilla { display: grid; gap: 14px; }
.rejilla.tres { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.rejilla.dos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* `minmax(0, 1fr)` también aquí: con `1fr` a secas, un <select> de opciones largas ensancha la columna
   y a 390 px el campo se sale de su tarjeta. */
@media (max-width: 720px) { .rejilla.tres, .rejilla.dos { grid-template-columns: minmax(0, 1fr); } }

input[type=text], input[type=email], input[type=password], input[type=number], input[type=tel],
input[type=search], input[type=date], select, textarea {
  width: 100%; height: 46px; padding: 0 13px; font-size: 15px; font-family: inherit;
  color: var(--texto); background: white; border: 1.5px solid var(--borde); border-radius: 8px;
}
textarea { height: 76px; padding: 11px 13px; line-height: 1.45; resize: vertical; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--marca); outline-offset: -1px; }
/* Anillo de foco de teclado para todo lo demás: sólo con `:focus-visible`, para que un clic con el
   ratón no lo pinte. Sobre la barra de marca el anillo es blanco: el de marca no se vería. */
a:focus-visible, button:focus-visible, summary:focus-visible, [tabindex]:focus-visible,
.principal:focus-visible, .secundaria:focus-visible, .paso:focus-visible,
.icono-solo:focus-visible, .miniatura:focus-visible {
  outline: 2px solid var(--marca); outline-offset: 2px;
}
.barra a:focus-visible, .barra summary:focus-visible, .barra button:focus-visible {
  outline-color: white;
}
[tabindex="-1"]:focus-visible { outline: none; }
/* Sin el retardo de doble toque del navegador en una pantalla táctil: en el TPV, «+» y «−» se
   pulsan seguidos. */
a, button, summary, .principal, .secundaria, .paso, .icono-solo, .miniatura {
  touch-action: manipulation;
}
input[readonly] {
  background: oklch(0.965 0.004 85); color: oklch(0.45 0.01 85); border-style: dashed;
}
.ayuda { font-size: 11.5px; color: var(--apagado); margin-top: 5px; line-height: 1.4; }
.requerido { color: var(--rojo); }
input.invalido, select.invalido, textarea.invalido { border-color: var(--rojo); }
.error-campo:empty { display: none; }
.error-campo { display: block; font-size: 11.5px; color: var(--rojo); margin-top: 5px; line-height: 1.4; }
/* Un aviso que no es un error (el IMEI que ya entró antes): en el mismo sitio que el error de un
   campo, en ámbar y no en rojo, y no marca el campo como inválido (`validacion.js`, `data-aviso`). */
.nota-suave { color: oklch(0.35 0.09 65); }
/* Una persona entre los resultados del buscador del alta de producto: un radio y sus datos, en una
   fila con marco (`campo(…, opcion=…, clase='opcion-persona')`). */
.opcion-persona {
  display: flex; align-items: center; gap: 11px; padding: 9px 11px; margin-bottom: 6px;
  border: 1px solid var(--borde-suave); border-radius: 8px; cursor: pointer;
}
.opcion-persona input[type=radio] { width: auto; height: auto; }
/* Un `<fieldset>` que sólo agrupa lo que se desactiva de golpe (el envío del TPV): sin marco. Sin
   `display` propio, para que su atributo `hidden` siga escondiéndolo. */
fieldset.sin-marco { border: 0; padding: 0; margin: 0; min-width: 0; }
/* Una casilla por línea dentro de un grupo de `campo()` de tipo `casillas` (las unidades de una devolución). */
.casilla-fila { display: flex; align-items: center; gap: 9px; margin-bottom: 6px; }
.enlace {
  border: none; background: none; padding: 0; font: inherit; font-size: 12px; cursor: pointer;
  color: var(--marca); text-decoration: underline;
}

/* Una fila de filtro de un solo campo con su botón (el «Día» de la caja): el botón mide lo que el control. */
.fila-filtro { display: flex; gap: 12px; align-items: flex-end; margin-bottom: 16px; }
.fila-filtro > .campo { min-width: 180px; }
.fila-filtro > button.secundaria { height: 46px; }

.unidades { display: flex; align-items: center; gap: 6px; }
/* Un control con algo al lado en la misma línea (la «€» tras el precio de la ficha): `campo(envolver=…)`. */
.fila-control { display: flex; align-items: center; gap: 6px; }
.unidades input { text-align: center; font-size: 18px; font-weight: 600; }
.paso {
  width: 42px; height: 46px; flex-shrink: 0; border: 1.5px solid var(--borde); border-radius: 8px;
  background: white; font-size: 20px; color: oklch(0.45 0.01 85); cursor: pointer;
}
.paso:hover:not(:disabled) { background: oklch(0.96 0.006 85); border-color: oklch(0.74 0.01 85); color: var(--texto); }

.acciones { display: flex; align-items: center; gap: 12px; margin-top: 6px; }
.crece { flex: 1; }
button.principal, .principal {
  height: 52px; padding: 0 26px; border: none; border-radius: 10px;
  background: var(--marca); color: white; font-size: 16px; font-weight: 600;
  font-family: inherit; cursor: pointer;
}
button.principal:hover:not(:disabled), a.principal:hover {
  background: color-mix(in oklch, var(--marca), black 14%);
}
button.secundaria, .secundaria {
  height: 52px; padding: 0 22px; border: 1.5px solid oklch(0.84 0.008 85); border-radius: 10px;
  background: white; color: oklch(0.42 0.01 85); font-size: 15px; font-weight: 600;
  font-family: inherit; text-decoration: none; display: inline-flex; align-items: center; cursor: pointer;
}
button.secundaria:hover:not(:disabled), a.secundaria:hover {
  background: oklch(0.96 0.006 85); border-color: oklch(0.74 0.01 85); color: var(--texto);
}
/* Lo irreversible (anular una tarjeta, suprimir o bloquear los datos de una persona): el botón que lo
   abre, en rojo apagado, y el que lo confirma, en rojo. Con dos clases para ganar a `button.principal`. */
.secundaria.peligro { color: var(--rojo); border-color: oklch(0.86 0.06 25); }
.secundaria.peligro:hover:not(:disabled) { background: var(--rojo-fondo); color: var(--rojo); border-color: var(--rojo); }
button.principal.peligro { background: var(--rojo); }
button.principal.peligro:hover:not(:disabled) { background: color-mix(in oklch, var(--rojo), black 14%); }
/* Un botón apagado no es del color de marca: gris liso, sin sombra y con el cursor de «no se puede».
   Va después de `button.principal` y `button.secundaria` (misma especificidad: manda la última).
   Los que ya tienen su propio apagado (iconos sueltos, TPV) siguen con el suyo. */
button:disabled:not(.icono-solo):not(.tpv-cantidad-boton):not(.tpv-boton) {
  background: oklch(0.92 0.005 85); color: oklch(0.45 0.01 85); cursor: not-allowed;
  border: 1.5px dashed oklch(0.80 0.008 85);
}

table { width: 100%; border-collapse: collapse; }
th {
  text-align: left; font-size: 11.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: oklch(0.54 0.01 85);
  padding: 10px 12px; border-bottom: 1px solid var(--borde);
}
td { padding: 12px; border-bottom: 1px solid var(--borde-suave); font-size: 14.5px; }
th.der, td.der { text-align: right; }
tr:last-child td { border-bottom: none; }
.reparto { font-size: 11.5px; color: var(--apagado); }
.vacio { padding: 40px 12px; text-align: center; color: var(--apagado); font-size: 15px; }
/* El detalle de un apartado del resumen (`_informe_detalle_tablas.html`): la cabecera de cada grupo,
   su subtotal —que es la fila del resumen— y el total del apartado. */
table.detalle td { padding: 8px 10px; font-size: 13.5px; }
table.detalle tr.grupo td { background: var(--fondo-suave); font-weight: 600; }
table.detalle tr.subtotal td { font-weight: 600; border-bottom: 2px solid var(--borde-suave); }
table.detalle tr.total td { font-weight: 700; }

.aviso {
  background: var(--rojo-fondo); border: 1px solid oklch(0.86 0.06 25);
  border-left: 4px solid var(--rojo); border-radius: 8px; padding: 12px 15px;
  font-size: 14px; color: var(--rojo); margin-bottom: 14px; line-height: 1.5;
}
/* Un aviso que informa de algo que no ha fallado (el cheque por encima del umbral, la deuda que nace
   de una devolución): mismo molde que `.aviso`, en ámbar y no en rojo. */
.aviso.informativo {
  background: var(--ambar-fondo); border-color: oklch(0.85 0.07 85);
  border-left-color: var(--ambar); color: oklch(0.35 0.09 65);
}
.hecho {
  background: oklch(0.96 0.03 155); border: 1px solid oklch(0.85 0.06 155);
  border-left: 4px solid var(--verde); border-radius: 8px; padding: 12px 15px;
  font-size: 14px; color: var(--verde); margin-bottom: 14px;
}

.pestanas { display: flex; border-bottom: 1px solid var(--borde); margin-bottom: 18px; }
.pestanas a {
  padding: 10px 4px; margin-right: 24px; font-size: 14.5px; font-weight: 600;
  color: var(--apagado); text-decoration: none; border-bottom: 2px solid transparent;
}
.pestanas a.activa { color: var(--texto); border-bottom-color: var(--marca); }
/* En pantalla estrecha las pestañas no caben en una línea (cuatro en la ficha del depositante): la
   barra se desplaza de lado dentro de sí misma, sin ensanchar la página, y cada pestaña conserva su
   texto en una sola línea. */
@media (max-width: 720px) {
  .pestanas { overflow-x: auto; scrollbar-width: thin; }
  .pestanas a { flex-shrink: 0; white-space: nowrap; }
}
/* Las de Ajustes (`_pestanas_ajustes.html`, hasta cinco) no se desplazan: a 390 px se cortaban «Tarjetas de
   socio» y «Exportar los datos» sin ninguna pista de que hubiera más. Bajan a una segunda línea, cada una
   entera. Es una clase aparte porque la ficha del depositante (`_pestanas_depositante.html`) sigue
   desplazándose, y no se toca. */
@media (max-width: 720px) {
  .pestanas.pestanas-envuelven { flex-wrap: wrap; overflow: visible; column-gap: 0; }
}

.entrar { max-width: 380px; margin: 12vh auto; }
.entrar .tarjeta { padding: 26px; }
.pie-alta { font-size: 13px; color: var(--apagado); line-height: 1.5; }

/* Pie legal de `base.html` (D1 de la auditoría legal): en el flujo, tras `<main>`, sin fijar. */
.pie-legal {
  max-width: 1180px; margin: 0 auto; padding: 0 20px 28px; font-size: 13px; color: var(--apagado);
  line-height: 1.5; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 20px;
}
.pie-legal a { color: inherit; }
.pie-legal a:hover { color: var(--texto); }

/* Páginas legales (`templates/legal/`): texto corrido, legible, con tablas que se desplazan en móvil. */
.legal { max-width: 860px; margin: 0 auto; padding: 24px 28px; line-height: 1.55; }
.legal h2 { font-size: 16px; margin: 22px 0 8px; }
.legal p, .legal li { font-size: 14.5px; }
.legal-version { color: var(--apagado); font-size: 13px; margin: 0 0 16px; }
.legal-acciones { font-size: 13px; }
.legal-datos { display: grid; grid-template-columns: max-content 1fr; gap: 4px 18px; margin: 8px 0; }
.legal-datos dt { color: var(--apagado); }
.legal-datos dd { margin: 0; }
.legal-primera-capa { font-size: 13.5px; line-height: 1.5; margin: 8px 0; }
.legal-tabla { overflow-x: auto; margin: 8px 0; }
.legal-tabla th, .legal-tabla td { text-align: left; vertical-align: top; }
/* `break-word` y no el `anywhere` de todas las celdas: con `anywhere` la columna se encoge hasta
   partir las palabras («Cuánt/o/dura», «Propi/a»). Así cada columna mide al menos su palabra más
   larga, y si la tabla no cabe se desplaza (`overflow-x` de arriba). */
.legal-tabla td { overflow-wrap: break-word; }
.legal-tabla th { white-space: nowrap; }

/* ─── Rediseño Opción A (21-ago) — piezas compartidas por toda la app ────────────────────────── */

/* Migas de pan: un nivel en una lista, dos en una ficha (`_migas.html`) */
.migas {
  display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--apagado);
  margin-bottom: 10px; flex-wrap: wrap; min-width: 0;
}
.migas > * { min-width: 0; }
.migas a { color: var(--apagado); text-decoration: none; font-weight: 500; }
.migas a:hover { color: var(--texto); text-decoration: underline; }
.migas .actual { color: var(--texto); font-weight: 600; }
.migas svg { opacity: 0.55; flex-shrink: 0; }

/* Enlace sobre texto normal (nombre de producto/depositante/contrato en una tabla o ficha) — nunca
   el azul subrayado por defecto del navegador. Lleva un subrayado tenue siempre: en una tableta no
   hay «pasar el ratón», y sin él nada dice que sea clicable. Al pasar el ratón se afirma. */
.enlace-contenido {
  color: inherit; text-decoration: underline; text-decoration-color: oklch(0.80 0.01 85);
  text-decoration-thickness: 1px; text-underline-offset: 3px;
}
.enlace-contenido:hover { text-decoration-color: currentColor; }

/* Avatar de iniciales: mismo círculo que ya usaba la barra superior, reutilizable en cualquier ficha */
.avatar {
  width: 36px; height: 36px; border-radius: 50%; background: oklch(0.93 0.015 85);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  font-size: 13px; font-weight: 600; color: oklch(0.42 0.02 85);
}
.avatar.grande { width: 52px; height: 52px; font-size: 17px; }

/* Cabecera de una pantalla: el título (con su subtítulo) a la izquierda y, a la derecha, lo que se hace
   con ella (botones) o su cifra. Era una fila en línea repetida en cada plantilla; en pantalla estrecha
   la fila no cabe, el título quedaba sin ancho (una letra por línea) y los botones, cortados. Ahí se
   apila: título arriba, a todo el ancho, y debajo las acciones. En pantalla ancha no cambia nada:
   `--alinear` y `--hueco` son los `align-items` y `gap` que cada plantilla ya tenía. */
.cabecera-pagina {
  display: flex; align-items: var(--alinear, flex-start); justify-content: space-between;
  gap: var(--hueco, 16px); margin-bottom: 16px;
}
.cabecera-pagina > :first-child { min-width: 0; }
.cabecera-pagina > :not(:first-child) { flex-shrink: 0; }
@media (max-width: 720px) {
  .cabecera-pagina { flex-direction: column; align-items: flex-start; gap: 12px; }
  .cabecera-pagina > :first-child { align-self: stretch; }
  .cabecera-pagina > :not(:first-child) { flex-shrink: 1; max-width: 100%; --justificar: flex-start; }
  .cabecera-pagina .cabecera-cifra { text-align: left; }
  .cabecera-pagina .cabecera-cifra .pie { justify-content: flex-start; }
}

/* Cifra protagonista de cabecera (precio hero de la ficha de producto) */
.cabecera-cifra { text-align: right; flex-shrink: 0; }
.cabecera-cifra .actual { font-size: 30px; font-weight: 700; color: var(--marca); }
.cabecera-cifra .anterior {
  font-size: 15px; color: oklch(0.62 0.01 85); text-decoration: line-through; margin-left: 9px;
}
.cabecera-cifra .pie {
  display: flex; align-items: center; gap: 8px; justify-content: flex-end; margin-top: 5px;
  font-size: 12.5px; color: var(--apagado);
}
.insignia {
  font-size: 11.5px; font-weight: 600; padding: 2px 8px; border-radius: 5px;
  background: var(--ambar-fondo); color: var(--ambar);
}

/* Barra proporcional del reparto de unidades */
.reparto-barra { display: flex; gap: 3px; height: 9px; border-radius: 5px; overflow: hidden; margin-bottom: 11px; }
.reparto-leyenda { display: flex; gap: 26px; flex-wrap: wrap; }
.reparto-leyenda .punto { width: 9px; height: 9px; border-radius: 3px; flex-shrink: 0; }
.reparto-leyenda .item { display: flex; align-items: center; gap: 8px; }

/* Cabecera de tabla ordenable */
th.ordenable a {
  display: inline-flex; align-items: center; gap: 5px; color: inherit; text-decoration: none;
  cursor: pointer;
}
th.ordenable a:hover { color: var(--texto); }
th.ordenable.activa a { color: var(--marca); }
th.ordenable svg { flex-shrink: 0; }
th.ordenable .indicador-neutro { opacity: 0.45; }

/* Botón con icono: icono+texto para acciones primarias, icono solo (con aria-label) para controles
   pequeños repetidos por fila — nunca icono solo en una acción primaria/infrecuente. */
.boton-icono {
  display: inline-flex; align-items: center; gap: 8px;
}
.boton-icono svg { flex-shrink: 0; }
/* La ficha de un producto: fotos a la izquierda y datos al lado; en pantalla estrecha, una debajo
   de otra (al lado, la columna de los datos se quedaba en 28 px y desbordaba la página). */
.ficha-producto { display: flex; gap: 16px; align-items: flex-start; }
.ficha-producto-fotos { width: 308px; flex-shrink: 0; }
.ficha-producto-cifras {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 16px;
  padding-top: 14px; border-top: 1px solid var(--borde-suave);
}
@media (max-width: 720px) {
  .ficha-producto { flex-direction: column; align-items: stretch; }
  .ficha-producto-fotos { width: auto; }
  .ficha-producto-cifras { grid-template-columns: repeat(2, 1fr); }
}
/* Una tabla ancha (el libro de caja, las ventas en curso del TPV) no ensancha la página en pantalla
   estrecha: su contenedor se desplaza de lado. Ahí cada columna mide al menos su palabra más larga
   (`break-word`, no el `anywhere` de todas las celdas, que la parte letra a letra) y el encabezado
   no se parte; en pantalla ancha no cambia nada.
   `relative` para que el texto `.sr-only` (absoluto) de un encabezado quede dentro y no ensanche
   la página. */
.tabla-desplazable { overflow-x: auto; position: relative; }
@media (max-width: 720px) {
  .tabla-desplazable td { overflow-wrap: break-word; }
  .tabla-desplazable th { white-space: nowrap; }
}
/* Los botones de una barra de acciones no parten su texto en dos líneas («Volver al listado» junto al
   precio y el mínimo de la ficha de un producto): lo que se estrecha es el aviso de al lado. */
.acciones > .boton-icono { white-space: nowrap; }
/* ...y en pantalla estrecha, donde ya no caben en una línea con el resto, la barra baja de línea. */
@media (max-width: 720px) {
  .acciones { flex-wrap: wrap; }
  /* Un botón largo («Añadir un producto (creará la versión 2)») no cabe en 350 px en una línea. */
  .acciones > .boton-icono { white-space: normal; max-width: 100%; }
}
.icono-solo {
  width: 30px; height: 30px; border-radius: 7px; border: 1.5px solid oklch(0.87 0.006 85);
  background: white; display: inline-flex; align-items: center; justify-content: center;
  color: oklch(0.5 0.01 85); cursor: pointer; padding: 0;
}
.icono-solo:hover { border-color: oklch(0.75 0.008 85); color: var(--texto); }
.icono-solo.peligro { color: var(--rojo); }
.icono-solo:disabled { opacity: 0.35; cursor: default; }
.icono-solo:disabled:hover { border-color: oklch(0.87 0.006 85); color: oklch(0.5 0.01 85); }

/* Fase G — carrusel de fotos de producto: una foto grande (la principal salvo que el visitante haya
   pulsado otra miniatura, ver `producto-fotos.js`) con miniaturas debajo y flechas para recorrerlas. */
.carrusel-fotos { display: flex; flex-direction: column; gap: 10px; margin: 10px 0 14px; }
.carrusel-grande {
  position: relative; width: 100%; aspect-ratio: 1 / 1; border-radius: 10px;
  overflow: hidden; border: 1px solid var(--borde); background: var(--borde-suave);
}
.carrusel-imagen { width: 100%; height: 100%; object-fit: cover; display: block; }
.carrusel-flecha {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 30px; height: 30px; border-radius: 50%; border: none;
  background: rgba(255, 255, 255, 0.85); color: var(--texto);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; padding: 0;
}
.carrusel-flecha:hover { background: white; }
.carrusel-flecha-izq { left: 8px; }
.carrusel-flecha-der { right: 8px; }

.carrusel-miniaturas { display: flex; flex-wrap: wrap; gap: 10px; }
.miniatura-envoltorio { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.miniatura {
  position: relative; width: 82px; height: 82px; padding: 0; border-radius: 7px;
  border: 2px solid transparent; overflow: hidden; cursor: pointer; background: none;
}
.miniatura img { width: 100%; height: 100%; object-fit: cover; display: block; }
.miniatura.activa { border-color: var(--marca); }
.miniatura-insignia {
  position: absolute; top: 3px; right: 3px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--marca); color: white; display: flex; align-items: center; justify-content: center;
}
.miniatura-controles { display: flex; gap: 3px; }

/* Fase E — slider de precio de doble tirador: dos <input type=range> superpuestos sobre una pista
   visual, cada uno visible sólo por su tirador (pointer-events sólo en el thumb, la pista es
   transparente para que se vean las dos a la vez). */
.rango-doble-pista { position: relative; height: 22px; margin-top: 8px; }
.rango-doble-pista::before {
  content: ""; position: absolute; top: 50%; left: 0; right: 0; height: 4px;
  background: var(--borde-suave); border-radius: 2px; transform: translateY(-50%);
}
.rango-doble {
  position: absolute; top: 0; left: 0; width: 100%; height: 22px; margin: 0;
  background: transparent; -webkit-appearance: none; appearance: none; pointer-events: none;
}
.rango-doble::-webkit-slider-runnable-track { background: transparent; }
.rango-doble::-moz-range-track { background: transparent; }
.rango-doble::-webkit-slider-thumb {
  -webkit-appearance: none; pointer-events: auto; width: 16px; height: 16px; margin-top: 3px;
  border-radius: 50%; background: var(--marca); cursor: pointer;
}
.rango-doble::-moz-range-thumb {
  pointer-events: auto; width: 16px; height: 16px; border-radius: 50%;
  background: var(--marca); border: none; cursor: pointer;
}

/* ─── TPV (T09a) ─────────────────────────────────────────────────────────────
   Pantalla de tablet según `diseno/TpvGuiado.dc.html`: pasos arriba, un campo de
   escaneo enorme que se lleva el foco, el carrito debajo y dos acciones grandes
   al pie. Reutiliza los tokens de arriba; sólo añade lo que es del TPV. */
.tpv-pantalla { max-width: 880px; margin: 0 auto; display: flex; flex-direction: column; gap: 20px; }
.tpv-pasos {
  display: flex; background: white; border: 1px solid var(--borde); border-radius: 11px;
  overflow: hidden;
}
.tpv-paso {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 15px 12px 12px; font-size: 16px; font-weight: 600; color: var(--apagado);
  border-bottom: 3px solid transparent;
}
.tpv-paso.activo { color: var(--texto); border-bottom-color: var(--marca); }
.tpv-paso-num {
  width: 26px; height: 26px; border-radius: 50%; background: oklch(0.91 0.006 85);
  color: oklch(0.50 0.01 85); display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700;
}
.tpv-paso.activo .tpv-paso-num { background: var(--marca); color: white; }

.tpv-escaneo { text-align: center; }
.tpv-titulo { font-size: 26px; font-weight: 600; margin: 8px 0 18px; }
.tpv-codigo {
  height: 84px; font-size: 26px; text-align: center; letter-spacing: 0.02em;
  border: 3px solid var(--marca); border-radius: 14px;
  box-shadow: 0 2px 14px oklch(0.24 0.012 85 / 0.07);
}
.tpv-codigo::placeholder { color: oklch(0.58 0.01 85); }
/* «Escanear con la cámara» (`_tpv_camara.html`): escondido hasta que `escaner-camara.js` sabe que hay
   cámara. `.secundaria` pone `display`, que ganaría al `hidden` del navegador. */
.tpv-camara { margin-top: 12px; }
.tpv-camara[hidden] { display: none; }
.tpv-camara-dialogo {
  width: min(560px, calc(100vw - 32px)); border: none; border-radius: 14px; padding: 18px;
  text-align: center;
}
.tpv-camara-dialogo::backdrop { background: oklch(0.2 0.01 85 / 0.6); }
.tpv-camara-video { width: 100%; max-height: 60vh; border-radius: 10px; background: black; }
.tpv-camara-estado { min-height: 1.4em; margin: 10px 0 14px; font-size: 18px; }

/* La ventana del cierre de caja (`_caja_conteo.html`, `caja-conteo.js`): el recuento de billetes y
   monedas. Columna con el cuerpo desplazable y el pie —total, motivo y botones— siempre a la vista. */
.caja-conteo-dialogo {
  width: min(880px, calc(100vw - 32px)); max-height: calc(100dvh - 32px);
  border: none; border-radius: 14px; padding: 0; color: var(--texto);
}
.caja-conteo-dialogo[open] { display: flex; flex-direction: column; }
.caja-conteo-dialogo::backdrop { background: oklch(0.2 0.01 85 / 0.6); }
.caja-conteo-titulo { margin: 0; padding: 16px 20px 12px; font-size: 20px; }
.caja-conteo-cuerpo { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 20px 12px; }
.caja-conteo-pie {
  flex: 0 0 auto; padding: 12px 20px 16px; background: var(--fondo-suave);
  border-top: 1px solid var(--borde);
}
.conteo-bloque-titulo { margin: 0 0 6px; font-size: 15px; }
.conteo-tabla th, .conteo-tabla td { padding: 3px 8px; font-size: 14px; }
.conteo-tabla thead th { padding-top: 4px; padding-bottom: 6px; }
.conteo-tabla th.conteo-valor { text-align: right; text-transform: none; letter-spacing: 0; font-size: 14.5px;
  color: var(--texto); white-space: nowrap; }
.conteo-tabla .conteo-cantidad { width: 42%; }
.conteo-tabla .conteo-cantidad input { height: 34px; text-align: right; }
.conteo-tabla tfoot th { text-transform: none; letter-spacing: 0; font-size: 13.5px; color: var(--texto);
  border-bottom: 0; }
.conteo-tabla tfoot td { font-weight: 600; border-bottom: 0; }
.conteo-resumen { display: flex; flex-wrap: wrap; gap: 6px 28px; margin: 0 0 12px; }
.conteo-resumen > div { display: flex; flex-direction: column; }
.conteo-resumen dt { font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--apagado); }
.conteo-resumen dd { margin: 0; font-size: 20px; font-weight: 600; }
.conteo-resumen .con-diferencia dd { color: var(--rojo); }
.caja-conteo-pie .requerido[hidden] { display: none; }
@media (max-width: 720px) {
  .caja-conteo-dialogo { width: calc(100vw - 16px); max-height: calc(100dvh - 16px); }
  /* En un móvil no caben las cantidades y el pie entero: se desplaza toda la ventana y sólo el
     resumen (total, esperado, diferencia) se queda pegado abajo. */
  .caja-conteo-dialogo[open] { display: block; overflow-y: auto; }
  .caja-conteo-titulo { padding: 12px 14px 8px; }
  .caja-conteo-cuerpo { padding: 0 14px 10px; overflow: visible; }
  .caja-conteo-pie { display: contents; }
  .caja-conteo-pie > * { margin-left: 14px; margin-right: 14px; }
  .caja-conteo-pie > .conteo-resumen {
    position: sticky; bottom: 0; z-index: 1; margin: 0; padding: 10px 14px;
    background: var(--fondo-suave); border-top: 1px solid var(--borde);
  }
  .caja-conteo-pie > .acciones { margin-bottom: 12px; }
}

/* El modal «Imprimir etiquetas» (`_etiquetas_dialogo.html`, `_etiquetas_paso.html`). */
.etiquetas-dialogo {
  width: min(760px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto;
  border: none; border-radius: 14px; padding: 20px 22px;
  /* Pegado arriba y no centrado: un aviso en línea que crece (un número fuera de rango) recentraría el
     diálogo y movería lo que hay encima del cursor entre pulsar y soltar, y el clic se perdería. */
  margin: 6vh auto auto;
}
.etiquetas-dialogo::backdrop { background: oklch(0.2 0.01 85 / 0.6); }
.etiquetas-titulo { margin: 0; font-size: 20px; }
.etiquetas-titulo:focus { outline: none; }
.etiquetas-resumen { font-size: 17px; margin: 8px 0 14px; }
.etiquetas-tabla th, .etiquetas-tabla td { padding: 8px 10px; text-align: left; vertical-align: top; }
.etiquetas-tabla th.num, .etiquetas-tabla td.num { text-align: right; }
.etiquetas-tabla .campo { margin: 0; }
.etiquetas-apagada { color: var(--apagado); }

.tpv-avisos { display: flex; flex-direction: column; gap: 8px; }
.tpv-vacio { text-align: center; color: var(--apagado); font-size: 16px; padding: 26px 0; margin: 0; }

.tpv-lineas { display: flex; flex-direction: column; gap: 12px; }
.tpv-linea {
  display: flex; align-items: center; gap: 18px; background: white;
  border: 1px solid var(--borde); border-radius: 12px; padding: 14px 20px;
}
.tpv-linea-datos { flex: 1; min-width: 0; }
.tpv-linea-nombre { font-size: 18px; font-weight: 600; }
.tpv-linea-codigo { font-size: 13.5px; color: var(--apagado); margin-top: 2px; }
.tpv-linea-cantidad {
  font-size: 14px; color: var(--apagado); white-space: nowrap;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
/* «−», el contador y «+» en una fila que no se parte: con `wrap`, el «+» caía debajo del «−». */
.tpv-cantidad-fila { display: flex; flex-wrap: nowrap; align-items: center; gap: 8px; }
/* «−» y «+» (F4): cuadrados, del tamaño de un dedo. */
.tpv-cantidad-boton { width: 36px; height: 36px; padding: 0; font-size: 20px; line-height: 1; }
.tpv-cantidad-boton:disabled { opacity: 0.45; cursor: not-allowed; }
.tpv-linea-libres { text-align: center; font-size: 12.5px; }
.tpv-linea-precio { font-size: 16px; color: var(--apagado); white-space: nowrap; }
.tpv-linea-subtotal { font-size: 22px; font-weight: 700; white-space: nowrap; }
.tpv-linea-quitar { display: flex; flex-direction: column; gap: 3px; align-items: flex-end; }
/* El precio acordado de una línea (S6), bajo ella, y la tarjeta de socio: un campo y su botón. */
.tpv-linea-acuerdo { padding: 6px 20px 4px; max-width: 420px; }
/* «Ficha → Acordado → Cobrado» bajo el nombre de la línea, y «Ver mínimo» bajo su precio (T1, D2). */
.tpv-linea-cifras { margin: 4px 0 0; }
.tpv-linea-minimo { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; margin-top: 4px; }
.tpv-tarjeta { display: flex; gap: 10px; align-items: flex-end; margin: 10px 0; }
.tpv-tarjeta > .campo { flex: 1; max-width: 320px; }
.tpv-descuento-socio .secundaria { margin-top: 10px; }

.tpv-total {
  display: flex; align-items: center; justify-content: space-between;
  background: white; border: 1px solid var(--borde); border-radius: 12px;
  padding: 16px 20px; font-size: 18px; font-weight: 600;
}
.tpv-total-importe { font-size: 26px; font-weight: 700; }

.tpv-acciones { display: flex; gap: 14px; }
/* Con dos clases: `button.principal` (0,1,1) le ganaba a `.tpv-boton` (0,1,0) y dejaba el botón a
   52 px junto a un «Cancelar» (`<a>`) de 68 px. */
.tpv-acciones .tpv-boton { height: 68px; font-size: 19px; }
.tpv-acciones .tpv-boton.principal { flex: 1; }
.tpv-acciones .tpv-boton:disabled { opacity: 0.45; cursor: not-allowed; }
/* La confirmación de «Cancelar» la venta (`_tpv_cancelar.html`) se pone encima del pie, a su ancho,
   con el texto a la izquierda y los botones a la derecha: colgando hacia arriba desde el botón
   tapaba la última línea del carrito y el total. Si no cabe en una franja (móvil), crece hacia
   abajo, nunca sobre el carrito. El `<details>` deja de ser la referencia (`static`) para que lo
   sea `.tpv-acciones`. Mientras está abierta, lo que tapa no asoma por sus esquinas redondeadas:
   ni el contorno de «Cancelar» abierto ni los otros botones del pie. */
.tpv-acciones { position: relative; }
.tpv-acciones > .tpv-cancelar { position: static; }
.tpv-acciones > .tpv-cancelar[open] > summary { outline: none; }
.tpv-acciones:has(> .tpv-cancelar[open]) > :not(.tpv-cancelar) { visibility: hidden; }
.tpv-cancelar > .detalle-cerrar-panel {
  left: 0; right: 0; top: 0; bottom: auto; width: auto; min-width: 0; max-width: none;
  min-height: 100%;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px;
}


/* ─── TPV (T09b): cliente, cobro y resumen ───────────────────────────────────
   Lo que añade el paso 2 sobre el paso 1: bloques de formulario a lo ancho,
   la búsqueda de cliente y las filas de cobro. El resumen reutiliza `.tpv-lineas`
   y `.tpv-total`, y añade el aviso de que esto no es un ticket. */
.tpv-bloque {
  background: white; border: 1px solid var(--borde); border-radius: 12px;
  padding: 16px 20px; margin-bottom: 14px;
}
.tpv-bloque-titulo { font-size: 13px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--apagado); margin: 0 0 12px; }
.tpv-error { margin-bottom: 14px; }
.tpv-buscar { font-size: 17px; }
.tpv-cliente-elegido { margin-top: 8px; font-size: 14px; color: var(--apagado); }
.tpv-cliente-elegido.con-cliente { color: var(--texto); font-weight: 600; }
.tpv-cliente-opcion {
  display: block; width: 100%; text-align: left; cursor: pointer; font: inherit;
  background: white; color: var(--texto); border: 1px solid var(--borde); border-radius: 8px;
  padding: 9px 11px; margin-top: 6px;
}
.tpv-cliente-opcion:hover { border-color: var(--marca); }
.tpv-cliente-opcion .reparto { margin-left: 6px; }
.tpv-canal { max-width: 260px; }
.tpv-cobro-cabecera, .tpv-cobro-fila {
  display: grid; grid-template-columns: 150px 130px 1fr 1fr; gap: 10px; align-items: center;
}
.tpv-cobro-cabecera {
  font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--apagado); margin-bottom: 6px;
}
.tpv-cobro-fila { margin-bottom: 8px; }
.tpv-cobro-fila input, .tpv-cobro-fila select { height: 42px; }
#tpv-anadir-cobro { margin-top: 4px; }
/* Descuento plegado: el `<summary>` es el título del bloque y dice lo que hay dentro, así que el
   bloque cerrado ya enseña si hay descuento. El título sigue siendo `.tpv-bloque-titulo`. */
:is(.tpv-descuento-plegable, .tpv-plegable) > summary { cursor: pointer; list-style: none; font-size: 15px; }
:is(.tpv-descuento-plegable, .tpv-plegable) > summary::-webkit-details-marker { display: none; }
:is(.tpv-descuento-plegable, .tpv-plegable) > summary::before { content: "▸ "; color: var(--apagado); }
:is(.tpv-descuento-plegable, .tpv-plegable)[open] > summary::before { content: "▾ "; }
:is(.tpv-descuento-plegable, .tpv-plegable) .tpv-bloque-titulo { display: inline; margin: 0; }
.tpv-descuento { display: flex; gap: 20px; align-items: flex-end; margin-top: 12px; }
.tpv-descuento > * { flex: 1; max-width: 260px; }
/* Con una ayuda o un error bajo un campo las columnas dejan de medir lo mismo: arriba, no al pie. */
.tpv-descuento.alinea-arriba { align-items: flex-start; margin-bottom: 14px; }
.tpv-a-cobrar { margin-bottom: 14px; }
/* Cada campo de una fila de cobro (`campo()`: control y su error) es una celda de la rejilla. */
.tpv-cobro-fila > .campo { min-width: 0; }
.tpv-pendiente { margin: 10px 0 0; font-size: 15px; color: var(--apagado); }
.tpv-pendiente[hidden] { display: none; }
.tpv-aviso-tope { margin: 10px 0 0; }
.tpv-aviso-tope[hidden] { display: none; }
/* «Entregado» y el cambio (A1): el campo es corto y el cambio, lo que se mira. */
.tpv-entregado { margin-top: 14px; }
.tpv-entregado[hidden] { display: none; }
.tpv-entregado .campo { max-width: 220px; }
.tpv-cambio { margin: 8px 0 0; font-size: 22px; font-weight: 700; min-height: 1.4em; }
.tpv-cambio.falta { color: var(--rojo); font-size: 16px; }
/* Pendiente ≠ 0: no sólo en rojo — también más peso; el texto «Pendiente: …» ya dice cuánto falta. */
.tpv-pendiente.descuadre { color: var(--rojo); font-weight: 700; }
/* El aviso de la toma (F3): el texto y «Seguir aquí» en la misma franja. */
.tpv-toma { display: flex; align-items: center; justify-content: space-between; gap: 14px;
  flex-wrap: wrap; }
.tpv-aviso-ticket {
  background: var(--ambar-fondo); border: 1px solid var(--ambar); border-radius: 12px;
  color: oklch(0.35 0.09 65); padding: 14px 18px; font-size: 15px; font-weight: 600;
}
.tpv-resumen-datos { margin: 0; display: flex; gap: 40px; flex-wrap: wrap; }
.tpv-resumen-datos dt { font-size: 11.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--apagado); margin-bottom: 3px; }
.tpv-resumen-datos dd { margin: 0; font-size: 16px; }
.tpv-cobros-lista { display: flex; flex-direction: column; gap: 8px; }
.tpv-cobro-ver { display: flex; align-items: center; gap: 16px; font-size: 15px; }
.tpv-cobro-metodo { font-weight: 600; min-width: 130px; }
.tpv-acciones .tpv-boton.principal { text-decoration: none; display: flex;
  align-items: center; justify-content: center; }

/* El TPV en un móvil o una tableta estrecha (390 px): la tienda cobra con ellos. A 1280 px nada de
   esto aplica. Lo guarda `tests/test_tpv_movil_navegador.py`, que mide la página, el nombre, los
   botones y los pasos. Antes: la línea era una fila sin salida (el nombre salía letra a letra) y la
   rejilla del cobro, de cuatro columnas fijas, ensanchaba la página. */
@media (max-width: 720px) {
  .tpv-paso { gap: 6px; padding: 13px 4px 10px; font-size: 14px; min-width: 0; }
  /* El nombre y el código ocupan el ancho de la tarjeta; cantidad, precio, subtotal y «Quitar»
     pasan a la fila de debajo. */
  .tpv-linea { flex-wrap: wrap; gap: 10px 14px; padding: 14px 16px; }
  .tpv-linea-datos { flex: 1 1 100%; }
  .tpv-linea-nombre { font-size: 17px; overflow-wrap: break-word; }
  .tpv-linea-cantidad { flex: 1 1 100%; flex-direction: row; align-items: center; gap: 12px; }
  .tpv-linea-precio { margin-right: auto; }
  .tpv-linea-subtotal { font-size: 20px; }
  .tpv-linea-acuerdo { padding: 6px 4px 4px; max-width: none; }
  .tpv-bloque { padding: 14px 16px; }
  .tpv-resumen-datos { gap: 14px 28px; }
  .tpv-tarjeta { flex-wrap: wrap; }
  .tpv-tarjeta > .campo { flex: 1 1 100%; max-width: none; }
  .tpv-canal { max-width: none; }
  .tpv-descuento { flex-wrap: wrap; gap: 12px; }
  .tpv-descuento > * { flex: 1 1 100%; max-width: none; }
  /* Cobro: método e importe en una fila; referencia y descripción, cada una a lo ancho debajo. La
     cabecera sobra: las etiquetas de cada campo (ocultas a 1280) se enseñan encima de él. */
  .tpv-cobro-cabecera { display: none; }
  .tpv-cobro-fila { grid-template-columns: 1fr 1fr; gap: 8px 10px; margin-bottom: 14px; }
  .tpv-cobro-fila > .campo:nth-child(n+3) { grid-column: 1 / -1; }
  .tpv-cobro-fila .sr-only {
    position: static; width: auto; height: auto; margin: 0 0 4px; overflow: visible;
    clip: auto; clip-path: none; white-space: normal;
  }
  .tpv-pendiente { margin-bottom: 14px; }
  .tpv-cobro-ver { flex-wrap: wrap; gap: 4px 12px; }
  .tpv-cobro-metodo { min-width: 0; }
  /* Los botones del pie, en filas que se parten; «Cobrar y terminar» no se sale. */
  .tpv-acciones { flex-wrap: wrap; gap: 10px; }
  .tpv-acciones .tpv-boton { height: 60px; font-size: 17px; flex: 1 1 140px; }
  .tpv-acciones .tpv-boton.principal { flex: 1 1 100%; }
  .tpv-acciones > .tpv-cancelar { flex: 1 1 140px; }
}

/* Filtro de periodo de /liquidaciones: el listado no se recarga al cambiar un selector (eso pediría
   un periodo por cada clic), así que se atenúa hasta que se pulse «Ver periodo», y el botón se
   marca como pendiente para que quede claro que hay un cambio sin aplicar. */
.listado-desactualizado {
  opacity: 0.38; pointer-events: none; transition: opacity 0.2s ease;
}
.periodo-modificado {
  border-color: var(--marca) !important; background-color: var(--fondo-suave) !important;
  font-weight: 600 !important;
}

/* Estado de carga. htmx pone `htmx-request` en el elemento que lanza la petición (los indicadores
   propios de htmx están apagados: `base.html`, `includeIndicatorStyles`). Un botón en curso no se
   vuelve a pulsar. */
.htmx-request { cursor: progress; }
form.htmx-request button, button.htmx-request { opacity: 0.6; pointer-events: none; }
/* Lo mismo para un formulario normal ya enviado: `validacion.js` lo marca para no enviarlo dos
   veces. El botón no se desactiva —perdería su `name`/`value`—, sólo se ve que está en curso. */
form[data-enviando="si"] button { opacity: 0.6; cursor: progress; }

/* Quien pide menos movimiento no ve la transición del listado que se atenúa. */
@media (prefers-reduced-motion: reduce) {
  .listado-desactualizado { transition: none; }
}

/* Selector de fotos (alta y ficha): dos botones —la cámara (sólo en táctil) y el equipo—, uno al
   lado del otro y apilados cuando no caben. Cada botón es el `<label for>` de un `<input
   type=file>` que se esconde SIN `display:none`: sigue enfocable (con teclado, y `validacion.js` le
   lleva el foco en un error). No es `.sr-only`: ése, al recibir el foco, se pinta como un enlace de
   salto. El anillo de foco se pinta en el botón, que es lo que se ve. */
.selector-fotos .fotos-controles { display: flex; flex-wrap: wrap; gap: 10px 12px; align-items: flex-start; }
.selector-fotos .fotos-control { position: relative; display: flex; flex-direction: column; min-width: 0; }
.selector-fotos .fotos-oculto {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); opacity: 0;
}
/* Ancho de botón y alto de dedo (>= 40 px); por encima del `height: 52px` de `.secundaria`. */
.selector-fotos .fotos-boton {
  height: 44px; padding: 0 18px; font-size: 14px; user-select: none; -webkit-user-select: none;
}
.selector-fotos .fotos-boton:hover {
  background: oklch(0.96 0.006 85); border-color: oklch(0.74 0.01 85); color: var(--texto);
}
.selector-fotos .fotos-oculto:focus-visible ~ .fotos-boton { outline: 2px solid var(--marca); outline-offset: 2px; }
.selector-fotos .fotos-oculto.invalido ~ .fotos-boton { border-color: var(--rojo); }
/* Subiendo: el input apagado (`hx-disabled-elt`) deja el botón sin efecto, y además se ve. */
.selector-fotos .fotos-oculto:disabled ~ .fotos-boton {
  opacity: 0.6; cursor: not-allowed; pointer-events: none;
}
/* El aviso va detrás del input (ahí lo busca `validacion.js`) pero se ve debajo del botón. */
.selector-fotos .fotos-control .error-campo { order: 2; max-width: 360px; }
.selector-fotos .fotos-subiendo { margin: 8px 0 0; font-size: 13px; }
.selector-fotos .fotos-subiendo:empty { display: none; }
/* La cámara sólo tiene sentido con un dedo: en un portátil `capture` no hace nada. */
.selector-fotos .solo-camara { display: none; }
@media (any-pointer: coarse) { .selector-fotos .solo-camara { display: block; } }
.fotos-elegidas { list-style: none; margin: 10px 0 0; padding: 0; }
.fotos-elegidas:empty { display: none; }
.fotos-elegidas li {
  display: flex; align-items: center; gap: 10px; padding: 6px 0; min-height: 52px; font-size: 13px;
  border-bottom: 1px solid var(--borde-suave);
}
.fotos-elegidas li:last-child { border-bottom: 0; }
.fotos-elegidas img {
  flex: 0 0 auto; width: 44px; height: 44px; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--borde-suave); background: oklch(0.96 0.006 85);
}
.fotos-elegidas .nombre { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fotos-elegidas .tamano { flex: 0 0 auto; color: var(--apagado); font-size: 12px; }
.fotos-elegidas button {
  flex: 0 0 auto; min-height: 32px; padding: 0 10px; border: 1px solid var(--borde); border-radius: 8px;
  background: white; color: var(--rojo); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.fotos-elegidas button:hover { background: var(--rojo-fondo); }

/* El portal del depositante (T22): una columna, pensada primero para el móvil. */
.portal { max-width: 560px; margin: 24px auto; display: grid; gap: 16px; }
.portal h2 { font-size: 16px; margin: 0 0 8px; }
.portal h3 { font-size: 15px; margin: 0 0 4px; }
.portal p { margin: 4px 0; }
.portal > .aviso { margin: 0; }  /* el hueco ya lo pone el `gap` de la rejilla */
.portal-cifra { font-size: 32px; font-weight: 700; margin: 4px 0 8px; }
.portal-producto { margin-bottom: 10px; }
.portal-lista { margin: 6px 0 0; padding-left: 18px; line-height: 1.7; }
.portal-salir { margin: 8px 0 0; }
/* Con el dedo, a 390 px: lo que se pulsa mide al menos 44 px de alto (los documentos, el resumen de
   «Cambios de precio» y el teléfono de la tienda). */
.portal-documentos { list-style: none; margin: 6px 0 0; padding: 0; }
.portal-documentos li + li { border-top: 1px solid var(--borde-suave); }
.portal-documentos a, .portal-tienda a[href^="tel:"] {
  display: flex; align-items: center; min-height: 44px; box-sizing: border-box;
}
.portal-precios > summary {
  cursor: pointer; min-height: 44px; box-sizing: border-box; padding-block: 10px; display: list-item;
}

/* La vista INTERNA del ticket (`app/ticket.py`, plan §4.7). Es el papel que saldría en la V2, no un
   ticket: la V1 no imprime nada que se lleve el cliente. */
.ticket-interno { max-width: 420px; margin-inline: auto; }
.ticket-papel {
  background: white; border: 1px solid var(--borde); border-radius: 6px; padding: 20px 22px;
  font-size: 15px; line-height: 1.5; display: flex; flex-direction: column; gap: 12px;
}
/* La marca DENTRO del papel: texto real, primero y último hijo del `<article>`, para que el modo lector,
   un recorte o copiar el papel no se lo lleven limpio; y una marca de agua diagonal repetida. El texto
   de la marca de agua sale de `data-marca` (la frase vive en `app/ticket.py`, no se repite aquí). */
.ticket-papel { position: relative; overflow: hidden; }
.ticket-marca-papel { text-align: center; font-size: 12px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: oklch(0.35 0.09 65); background: var(--ambar-fondo);
  border: 1px solid var(--ambar); border-radius: 6px; padding: 4px 8px; }
.ticket-papel::before {
  content: attr(data-marca) "   ·   " attr(data-marca) "   ·   " attr(data-marca) "   ·   " attr(data-marca)
    "   ·   " attr(data-marca) "   ·   " attr(data-marca) "   ·   " attr(data-marca) "   ·   "
    attr(data-marca) "   ·   " attr(data-marca) "   ·   " attr(data-marca) "   ·   " attr(data-marca);
  position: absolute; inset: -40%; transform: rotate(-30deg); pointer-events: none; user-select: none;
  font-size: 22px; font-weight: 700; line-height: 3.2; text-transform: uppercase; text-align: center;
  color: oklch(0.35 0.09 65 / 0.09); z-index: 0;
}
.ticket-papel > * { position: relative; z-index: 1; }
.ticket-cabecera { text-align: center; }
.ticket-nombre { font-size: 22px; font-weight: 700; letter-spacing: 0.02em; margin-bottom: 4px; }
.ticket-lineas { border-top: 1px solid var(--borde); border-bottom: 1px solid var(--borde);
  padding: 10px 0; display: flex; flex-direction: column; gap: 10px; }
.ticket-linea-codigo { font-weight: 600; }
.ticket-linea-fila, .ticket-fila { display: flex; justify-content: space-between; gap: 16px; }
.ticket-linea-detalle { color: var(--apagado); font-size: 13px; }
.ticket-importe { white-space: nowrap; }
.ticket-total { font-weight: 700; }
.ticket-gracias { text-align: center; font-weight: 600; letter-spacing: 0.04em;
  border-top: 1px solid var(--borde); border-bottom: 1px solid var(--borde); padding: 8px 0; }
.ticket-pie { display: flex; flex-direction: column; gap: 4px; }
.ticket-ayuda { margin: 14px 0 0; }

/* Imprimir esta página no debe dar un ticket: sólo queda la marca, a toda hoja, y sin la barra de la
   aplicación. Impedir `Ctrl+P` del todo no se puede; facilitarlo, no se debe. */
@media print {
  body:has(.ticket-interno) > header { display: none !important; }
  .ticket-interno .ticket-papel, .ticket-interno .ticket-ayuda { display: none !important; }
  .ticket-interno .ticket-marca { display: block !important; font-size: 28px; text-align: center;
    border-width: 3px; padding: 40px 20px; }
  /* El resumen de la venta tampoco se imprime: sólo el cartel «esta pantalla no es un ticket». */
  body:has(.tpv-resumen-venta) > header { display: none !important; }
  .tpv-resumen-venta > :not(.tpv-aviso-ticket) { display: none !important; }
  .tpv-resumen-venta .tpv-aviso-ticket { font-size: 28px; text-align: center; border-width: 3px;
    padding: 40px 20px; }
}

/* ─── Listados de ventas y devoluciones en pantalla estrecha ─────────────────────────────────────
   Una tabla de seis columnas no cabe en 390 px: el `overflow-wrap: anywhere` de las celdas parte los
   nombres en trozos de cinco letras y las cabeceras quedan de una letra por línea. Aquí cada fila
   pasa a ser una tarjeta: el producto arriba, entero, y debajo cada dato con su rótulo (el
   `data-etiqueta` de la celda). En pantalla ancha no cambia nada. La cabecera se queda en el árbol de
   accesibilidad, fuera de la vista. */
@media (max-width: 720px) {
  .tabla-tarjetas, .tabla-tarjetas tbody, .tabla-tarjetas tfoot, .tabla-tarjetas tr, .tabla-tarjetas td {
    display: block; width: auto;
  }
  .tabla-tarjetas thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .tabla-tarjetas tr { padding: 10px 14px; border-bottom: 1px solid var(--borde-suave); }
  .tabla-tarjetas tr:last-child { border-bottom: none; }
  .tabla-tarjetas td { padding: 2px 0; border: 0; text-align: right; overflow-wrap: break-word; }
  .tabla-tarjetas td[data-etiqueta] { display: flex; justify-content: space-between; gap: 14px; }
  .tabla-tarjetas td[data-etiqueta]::before {
    content: attr(data-etiqueta); flex: none; text-align: left; font-size: 11.5px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.06em; color: var(--apagado); padding-top: 2px;
  }
  .tabla-tarjetas .valor { min-width: 0; text-align: right; overflow-wrap: anywhere; }
  .tabla-tarjetas td.titular { order: -1; text-align: left; font-weight: 600; padding-bottom: 4px; }
  .tabla-tarjetas tr { display: flex; flex-direction: column; }
  .tabla-tarjetas td.vacio { padding: 28px 0; text-align: center; }
  /* Un dato en letra pequeña de tabla (`.reparto`, 11.5 px) no desentona con su rótulo en una tarjeta. */
  .tabla-tarjetas td.reparto { font-size: 13px; }
  /* Un dato sin valor (un motivo que no se escribió) no pinta su rótulo solo. */
  .tabla-tarjetas td:has(> .valor:empty) { display: none; }
  /* La cabecera de una tabla ordenable no se esconde: ordenar es una función, no un adorno. Queda como
     una fila de enlaces «Ordenar por …» encima de las tarjetas; las columnas que no ordenan, fuera. */
  .tabla-tarjetas:has(th.ordenable) thead {
    position: static; width: auto; height: auto; overflow: visible; clip: auto; white-space: normal;
    display: block; padding: 6px 14px; border-bottom: 1px solid var(--borde-suave);
  }
  .tabla-tarjetas:has(th.ordenable) thead tr {
    display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 0 16px;
    padding: 0; border: 0;
  }
  .tabla-tarjetas:has(th.ordenable) thead tr::before {
    content: "Ordenar por"; font-size: 11.5px; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.06em; color: var(--apagado);
  }
  .tabla-tarjetas thead th.ordenable { display: block; padding: 0; border: 0; }
  .tabla-tarjetas thead th.ordenable a { padding: 8px 0; }
  .tabla-tarjetas thead th:not(.ordenable) { display: none; }
}

/* ─── Liquidaciones y clientes en pantalla estrecha ───────────────────────────────────────────────
   Las mismas tarjetas de arriba (`.tabla-tarjetas`) más lo que sólo estas pantallas tienen:
   - `.form-excepcion`: el motivo y «Conceder excepción» de una venta fuera de plazo. En ancho son una
     fila, campo y botón; en estrecho, uno encima del otro y a todo el ancho de la tarjeta (en fila el
     campo quedaba en vertical y el botón se salía).
   - `.acciones-celda`: la celda de una fila que sólo lleva botones («Detalles», «Conceder excepción»),
     sin rótulo; en la tarjeta queda separada de los datos.
   - `.recibo-enlace`: el icono de descargar el recibo, de 18 px, se pulsa con el dedo: un objetivo mayor.
   - `.acciones-ficha`: «Ver ficha de depositante» y «Editar» de la ficha de un cliente, uno por línea. */
.form-excepcion { margin: 0; display: flex; gap: 8px; align-items: flex-end; justify-content: flex-end; }
@media (max-width: 720px) {
  .tabla-tarjetas td.acciones-celda { padding-top: 8px; }
  .form-excepcion { flex-direction: column; align-items: stretch; text-align: left; }
  .form-excepcion button { width: 100%; }
  .recibo-enlace { min-width: 40px; min-height: 40px; justify-content: center; }
  .acciones-ficha { flex-direction: column; align-items: stretch; }
  .acciones-ficha > .crece { display: none; }
  .acciones-ficha > .boton-icono { justify-content: center; }
}

/* ─── Auditoría, Usuarios, Informes y Tarjetas de socio en pantalla estrecha ───────────────────────
   Las mismas tarjetas de arriba (`.tabla-tarjetas`) más lo que sólo estas pantallas tienen:
   - `.acciones-fila`: los botones de un usuario («Editar», «Nueva contraseña», «Dar de baja») son una sola
     línea sin partir en pantalla ancha (`nowrap`, a la derecha); en una tarjeta de ~330 px no caben, así
     que pasan a varias líneas, alineados a la izquierda. Cada botón sigue sin partir su texto.
   - `tfoot`: la fila de totales de un informe es una tarjeta más, con fondo para distinguirla.
   - `.casilla-con-ayuda`: una casilla de permiso con su línea de ayuda debajo (`/usuarios`), una por línea. */
.casilla-con-ayuda { margin: 8px 0 12px; }
.casilla-con-ayuda > .casilla-etiqueta { display: flex; align-items: center; gap: 8px; margin: 0; }
.casilla-con-ayuda > .ayuda { margin: 2px 0 0 26px; }
@media (max-width: 720px) {
  .tabla-tarjetas .acciones-fila { flex-wrap: wrap; justify-content: flex-start; }
  /* La celda `.der` alinea a la derecha: con una confirmación abierta, el botón que la abrió (a todo el
     ancho de la fila) se iba al lado contrario que sus hermanos. */
  .tabla-tarjetas td.acciones-celda { text-align: left; }
  .tabla-tarjetas tfoot tr { background: var(--fondo); border-top: 1px solid var(--borde); }
}
