/* ==========================================================================
   Sistema de Diseño RentitSpain — hoja central única.
   Tokens (claro/oscuro) + acento elegible por el usuario + componentes.
   Todo deriva de --accent con color-mix, así el color se cambia en un sitio.
   ========================================================================== */

/* ---- Neutros: modo claro ---- */
:root{
  --bg:#f6f7f9; --surface:#ffffff; --surface-2:#f1f3f6; --panel:#ffffff;
  --border:#e6e8ec; --border-strong:#d6dae1;
  --text:#14171c; --text-2:#5b6270; --text-3:#8b91a0;
  --success:#15803d; --warning:#b45309; --danger:#c4362c;
  --r-sm:6px; --r-md:10px; --r-lg:14px; --pill:999px;
  --sh-1:0 1px 2px rgba(20,23,28,.05); --sh-2:0 6px 22px rgba(20,23,28,.09); --sh-3:0 18px 44px rgba(20,23,28,.16);
  --sb:236px;
  --accent:#2563eb; /* por defecto: azul */
  /* color-scheme le dice al NAVEGADOR en qué modo pintar lo que dibuja ÉL y el CSS no alcanza: la lista
     desplegable de los <select>, los selectores de fecha, las barras de scroll y el autorrelleno. */
  color-scheme:light;
}
/* ---- Neutros: modo oscuro ---- */
[data-theme="dark"]{
  --bg:#0f1216; --surface:#171b22; --surface-2:#1e232c; --panel:#1b2029;
  --border:#272d38; --border-strong:#39424f;
  /* JERARQUÍA DE TEXTO EN NOCHE (15/07 — corrige el AJUSTE 3 previo, que había subido TODO --text-2 a
     #ededed y aplanado la jerarquía). Dos niveles de lectura:
       --text   = #ededed → PRIMERA lectura / importante: títulos de página y encabezados, contenido
                  principal de tablas y listados (td), valores numéricos importantes. Alineado con los
                  labels del sidebar (regla .sidebar más abajo, también #ededed).
       --text-2 = #a3aab8 → SECUNDARIO: subtítulos y descripciones de apoyo, timestamps/fechas de contexto,
                  ayuda/placeholders, contadores de contexto, estados deshabilitados, cabeceras de columna.
     Los DOS tienen contraste de sobra sobre el fondo oscuro (#a3aab8 ≈7.7:1, #ededed ≈15.4:1): la
     diferencia es de JERARQUÍA de lectura, no de accesibilidad. --text-3 (terciario) sin cambios. El valor
     de DÍA (:root → --text-2:#5b6270) NO se toca. */
  --text:#ededed; --text-2:#a3aab8; --text-3:#727a89;
  --success:#3ddc97; --warning:#f0b429; --danger:#ff6f62;
  --sh-1:none; --sh-2:0 8px 24px rgba(0,0,0,.45); --sh-3:0 18px 44px rgba(0,0,0,.6);
  /* 🔴 FIX 17/07 (lo cazó Gabriel con una captura): sin esto el navegador seguía pintando en CLARO lo que
     dibuja él, así que la LISTA de todos los <select> salía BLANCA mientras el texto de las <option>
     heredaba --text (#ededed) → LETRA BLANCA SOBRE BLANCO. Afectaba a los ~175 desplegables de la app
     desde que existe el modo oscuro, no solo a los canales. Medido antes del fix: color-scheme de la raíz
     = "normal" y option.color = rgb(232,238,243). Con color-scheme:dark el navegador pinta la lista
     oscura y el texto se lee. Arregla de paso date pickers, scrollbars y autorrelleno. */
  color-scheme:dark;
}
/* ---- Acentos elegibles por el usuario ---- */
[data-accent="azul"]{--accent:#2563eb}
[data-accent="teal"]{--accent:#0d9488}
[data-accent="indigo"]{--accent:#4f46e5}
[data-accent="esmeralda"]{--accent:#059669}
/* RentitSpain = el acento de MARCA (16/07). El teal del logo/correos es #1786a0, pero con texto blanco
   encima (--on-accent:#fff, que usan .btn.primary y los chips de acento) da 4,29:1 → NO llega al 4,5:1
   de AA. Se usa el mismo teal un punto más oscuro: #0f6d84 = 5,81:1 con blanco (mejor incluso que el azul
   por defecto, 5,12:1) y misma familia de color a la vista. La marca completa (marino #103a5c + naranja
   #f5821f) NO entra aquí: eso tocaría fondos y jerarquía de acción → decisión aparte. */
[data-accent="rentit"]{--accent:#0f6d84}

/* ==========================================================================
   PIEL DE MARCA «RentitSpain» — propuesta 2 COMPLETA (Gabriel, 16/07).
   Se activa SOLO con el acento «rentit» (hoy el de por defecto). Elegir cualquier
   otro acento (azul/teal/indigo/esmeralda) devuelve la app a los neutros grises de
   siempre → interruptor de ida y vuelta, sin maquinaria nueva y sin tocar pantallas.
   Tres capas: (1) acento teal ↑ · (2) neutros con sesgo marino ↓ · (3) densidad ↓.
   La acción principal en naranja va en el bloque de .btn.primary, más abajo.
   ========================================================================== */

/* (2) NEUTROS CON SESGO DE MARINO (#103a5c, el de la marca). No se leen como "color":
   se leen como que el sitio tiene dueño — un gris puro es el que sale cuando nadie
   eligió. Medido antes de hacerlo: la app usa var(--surface) en 109 sitios y solo
   tiene 107 fondos con hex fijo, de los cuales la mayoría son los documentos
   (liquidación/correos/NDA) que DEBEN quedarse en blanco con letra oscura (regla
   .paper). Por eso el cambio de fondo llega entero y no deja la app a medias.
   Ojo al ORDEN/especificidad: [data-theme="dark"] es (0,1,0) y está definido más
   arriba; por eso la variante de día lleva :not([data-theme="dark"]) y la de noche
   encadena los dos atributos — así ninguna de las dos pisa al modo oscuro. */
[data-accent="rentit"]:not([data-theme="dark"]){
  --bg:#eef2f5; --surface:#ffffff; --surface-2:#eaf1f5; --panel:#ffffff;
  --border:#d7e3ea; --border-strong:#bfd1dc;
  --text:#0b1620; --text-2:#4c6475; --text-3:#7d94a4;
}
[data-accent="rentit"][data-theme="dark"]{
  --bg:#0b1620; --surface:#101f2b; --surface-2:#16293a; --panel:#132534;
  --border:#1d3446; --border-strong:#264458;
  --text:#e8eef3; --text-2:#8ba2b4; --text-3:#5d7488;
}

/* (3) DENSIDAD — SOLO ESCRITORIO (>860px, el complementario exacto del breakpoint
   móvil del sistema). En MÓVIL no se toca nada: los botones siguen a 40px porque
   gestores y limpiadoras trabajan con el dedo y en la calle — es la razón de ser del
   modo Light. Apretar también el móvil haría la app más bonita en el escritorio y
   PEOR en el bolsillo de quien la usa. Decisión de ergonomía, no de gusto. */
@media(min-width:861px){
  [data-accent="rentit"]{--sb:200px;--r-lg:10px;--r-md:8px}
  [data-accent="rentit"] .btn{min-height:34px;font-size:14px;padding:0 13px}
  [data-accent="rentit"] .btn.sm{min-height:30px;font-size:13px;padding:0 10px}
  [data-accent="rentit"] .btn.lg{min-height:40px;font-size:15px;padding:0 16px}
  [data-accent="rentit"] .card{padding:15px 17px}
  [data-accent="rentit"] .card-head{margin-bottom:12px}
  [data-accent="rentit"] .tbl th{padding:8px 12px}
  [data-accent="rentit"] .tbl td{padding:9px 12px;font-size:13.5px}
  [data-accent="rentit"] .nav-item{padding:7px 9px;font-size:13.5px}
  [data-accent="rentit"] .wrap{padding:18px 20px}
  [data-accent="rentit"] .pagebar h1{font-size:21px}
}
/* ---- Derivados de color (auto claro/oscuro con color-mix) ---- */
:root{
  --accent-weak:color-mix(in srgb, var(--accent) 13%, var(--surface));
  --accent-weak2:color-mix(in srgb, var(--accent) 26%, var(--surface));
  --accent-strong:color-mix(in srgb, var(--accent) 85%, #000);
  --accent-text:color-mix(in srgb, var(--accent) 82%, var(--text));
  --on-accent:#fff;
  --ok-weak:color-mix(in srgb, var(--success) 15%, var(--surface));
  --ok-text:color-mix(in srgb, var(--success) 72%, var(--text));
  --warn-weak:color-mix(in srgb, var(--warning) 17%, var(--surface));
  --warn-text:color-mix(in srgb, var(--warning) 74%, var(--text));
  --danger-weak:color-mix(in srgb, var(--danger) 15%, var(--surface));
  --danger-text:color-mix(in srgb, var(--danger) 72%, var(--text));
}
/* 🔴 CONTRASTE EN NOCHE del texto de acento (21/07). UNA sola variable y SOLO en modo oscuro.
   QUÉ PASABA: --accent-text se calcula igual en los dos temas (acento 82 % + --text 18 %). De DÍA eso
   OSCURECE el acento (--text es casi negro) → contrasta de sobra. De NOCHE hace lo contrario de lo que
   hace falta: --text es casi blanco, pero con solo un 18 % apenas aclara, así que el texto se queda casi
   tan oscuro como el acento y se pega al fondo. MEDIDO en las 10 combinaciones vivas (5 acentos × 2 temas)
   sobre --accent-weak, que es el fondo de la PESTAÑA/BOTÓN ACTIVO y del chip de acento:
     azul 3,95 · teal 4,88 · indigo 3,44 · esmeralda 4,88 · rentit 3,52  → tres por debajo del 4,5 de AA.
     Peor aún: sobre --surface (o sea, TODOS los enlaces) azul 4,41 · indigo 3,77 · rentit 3,92.
   CÓMO LO HACEN LOS BUENOS (Material 3, Radix, GitHub Primer): el rol «texto de acento» NO es el mismo
   color en los dos temas — en claro se oscurece el acento y en oscuro se ACLARA (M3: primario tono 40 de
   día, tono 80 de noche). Aquí eso se consigue subiendo el peso de --text en la mezcla, que de noche ES
   el color claro. 58 % de acento es el punto elegido: es el valor MÁS ALTO (= el que menos se aleja del
   82 % de siempre y mejor conserva el color de marca) que deja TODAS las parejas de acento del modo
   oscuro por encima de 4,5:1 con margen — sobre --accent-weak (13 %), sobre la mezcla al 19 % de
   limpieza.php, sobre --accent-weak2 (26 %, el hover de .btn.accentsoft) y sobre --surface:
     peor caso 4,77:1 (rentit sobre --accent-weak2) · pestaña activa: azul 5,77 · teal 6,48 · indigo 5,33 ·
     esmeralda 6,50 · rentit 5,38. A partir del 62 % el hover de indigo/rentit vuelve a caer por debajo de AA.
   ⚠️ El MODO CLARO no se toca: esta regla vive dentro de [data-theme="dark"] y en día ni se aplica.
   ⚠️ Va DESPUÉS del :root de arriba a propósito: [data-theme="dark"] y :root tienen la MISMA
   especificidad (0,1,0) y ambos caen en <html> (el JS pone data-theme en documentElement), así que quien
   manda es el que va DESPUÉS en el fichero. Si esta regla se sube al bloque de neutros oscuros de la
   cabecera, el :root de aquí la pisaría y el arreglo dejaría de existir sin avisar. */
[data-theme="dark"]{
  --accent-text:color-mix(in srgb, var(--accent) 58%, var(--text));
}
/* ---- Compatibilidad con pantallas aún no migradas (alias de tokens antiguos) ---- */
/* Así heredan la nueva paleta y el modo oscuro sin tocarlas todavía. */
:root{
  --card:var(--surface); --bd:var(--border); --bd2:var(--border-strong);
  --tx:var(--text); --tx2:var(--text-2); --tx3:var(--text-3);
  --info:var(--accent-text); --infobg:var(--accent-weak);
  --ok:var(--ok-text); --okbg:var(--ok-weak);
  --warn:var(--warn-text); --warnbg:var(--warn-weak);
  --dangerbg:var(--danger-weak); --rad:var(--r-lg);
}
/* ==========================================================================
   ⚠️ REGLA DE CONTRASTE (OBLIGATORIA) — que NUNCA haya texto ilegible en claro NI oscuro.
   NO mezclar un color FIJO (#fff, #000, hex) con un color de TEMA (var(--...)) entre el
   fondo y el texto de un mismo elemento. Elegir UN solo mundo:
   • UI (tabs, botones, chips, tarjetas, menús): SIEMPRE tokens del tema →
       fondo  var(--surface) / var(--surface-2)
       texto  var(--text) / var(--text-2)
       activo var(--accent) + texto var(--on-accent)
     Así fondo y texto cambian JUNTOS con el modo claro/oscuro.
   • "Hoja" / documento (previews de correo·factura·liquidación, PDFs, el email):
       fondo blanco fijo + TODO el texto en color OSCURO FIJO. Usar la clase .paper
       (ya fija los colores). NUNCA var(--text) dentro de una hoja de fondo blanco fijo.
   Única excepción válida: texto #fff sobre var(--accent) (color saturado) — contrasta siempre.

   ➕ COROLARIO (21/07, tras medirlo): elegir «el mismo mundo» NO garantiza el contraste por sí solo. Un
   token derivado del acento puede cumplir la regla de arriba y aun así quedarse corto en UNO de los dos
   temas, porque la MISMA fórmula produce efectos opuestos en claro y en oscuro (era el caso de
   --accent-text: ver el bloque [data-theme="dark"] de más arriba). Norma práctica: cuando un token de
   TEXTO se derive del acento, MÍDELO en los 5 acentos × 2 temas contra el fondo real sobre el que se pinta
   (--surface, --accent-weak y --accent-weak2) y, si en un tema no llega, corrige SOLO ese tema.
   Umbrales: texto 4,5:1 · superficie/color informativo (barra, cuadradito de leyenda) 3:1.
   ========================================================================== */
table{width:100%;border-collapse:collapse}
th,td{text-align:left;padding:9px 10px;border-bottom:1px solid var(--border);font-size:14px;color:var(--text)}
th{color:var(--text-2);font-weight:500}
.brand{font-weight:600;font-size:16px}
.metric .l{font-size:12.5px;color:var(--text-2)}
.metric .v{font-size:26px;font-weight:700;margin-top:4px}

/* ---- Reset / base ---- */
*{box-sizing:border-box}
html,body{margin:0}
/* El fondo TAMBIÉN en la raíz (31/07): en cada recarga el navegador pinta el lienzo del html antes que
   el body → sin esto, fotograma blanco (fogonazo) en modo oscuro al aplicar filtros. Va con la
   <meta name="color-scheme"> de layout.php, que cubre el instante ANTES de que este CSS cargue. */
html{background:var(--bg)}
body{background:var(--bg);color:var(--text);font-family:-apple-system,"Segoe UI",Roboto,Inter,Arial,sans-serif;line-height:1.55;font-size:15px;-webkit-font-smoothing:antialiased}
a{color:var(--accent-text);text-decoration:none}
a:hover{text-decoration:underline}
h1,h2,h3,h4{margin:0;color:var(--text);line-height:1.25}
h1{font-size:25px;font-weight:700} h2{font-size:20px;font-weight:600} h3{font-size:16px;font-weight:600} h4{font-size:15px;font-weight:600}
.muted{color:var(--text-2)}
/* ---- Tono del TEXTO (utilidades) — la familia de .muted, completada el 28/07 ----
   POR QUÉ EXISTEN: no hay ninguna clase suelta que tiña texto de rojo/verde/ámbar. Solo existen
   .btn.danger, .chip.danger, .alert.danger y .ddmenu button.danger, o sea el color SIEMPRE atado a un
   componente. Consecuencia real medida en la auditoría del 28/07: `<td class="danger">` y
   `<b class="danger">` NO pintaban nada — facturas2_comparar.php prometía «Están abajo, en rojo» y
   salía en negro. Con .muted ya pasaba lo contrario (sí existe), así que un aviso menor se veía MÁS
   que un fallo. Todos los sistemas grandes tienen esta pieza (Bootstrap .text-danger, GitHub Primer
   .color-fg-danger, Shopify Polaris tone="critical"): aquí faltaba.
   CUÁNDO USARLAS: texto corrido o celda de tabla que hay que teñir. Para una etiqueta de estado,
   antes el chip (.chip.ok/.warn/.danger), que además lleva forma y fondo.
   ⚠️ El color NUNCA puede ser lo único que lo distingue (WCAG 1.4.1): la palabra o la cifra tienen
   que decirlo igual. Estas clases son REFUERZO.
   Medido sobre --surface, --surface-2 y --bg en las 10 combinaciones (5 acentos × claro/oscuro):
   peor caso 6,44:1 — mínimo de texto 4,5:1. No pisan a nadie: el prefijo `.text-` no existía en la
   hoja y ninguna pantalla usaba estos nombres.
   🔴 POR QUÉ LLEVAN !important (y no es pereza). Sin él NO funcionan dentro de una tabla, que es justo
   donde hacen falta: `.tbl td` (más abajo) es un selector MÁS específico —clase + etiqueta— que una
   clase suelta, así que su `color:var(--text)` ganaba y la celda seguía saliendo en negro. Probado en
   Chrome antes de ponerlo: la columna Δ de facturas2_comparar medía 14,33 (o sea, --text) en vez de
   los 7,64 del rojo. Así lo hacen los dos sistemas grandes por el mismo motivo: Bootstrap marca TODAS
   sus utilidades con !important a propósito, y GitHub Primer también (.color-fg-danger). Una utilidad
   que a veces pinta y a veces no es peor que no tenerla: fue la causa exacta del fallo del 28/07. */
.text-ok{color:var(--ok-text)!important}
.text-warn{color:var(--warn-text)!important}
.text-danger{color:var(--danger-text)!important}
.tnum{font-variant-numeric:tabular-nums}
.mono{font-family:ui-monospace,Menlo,Consolas,monospace}

/* ---- App shell: sidebar + contenido ---- */
.app{display:flex;min-height:100vh}
.sidebar{width:var(--sb);flex:none;background:var(--surface);border-right:1px solid var(--border);
  display:flex;flex-direction:column;padding:14px 12px;position:sticky;top:0;height:100vh;gap:2px;overflow-y:auto}
.sb-brand{display:flex;align-items:center;gap:9px;padding:4px 8px 12px;font-weight:600;font-size:15px}
.sb-logo{width:28px;height:28px;border-radius:8px;background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:600}
.nav-group{font-size:12px;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:var(--text-3);padding:13px 10px 4px}
.nav-acc{display:flex;flex-direction:column;gap:2px}
.nav-group-h{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;background:none;border:0;cursor:pointer;font-family:inherit;font-size:12px;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:var(--text-3);padding:13px 10px 5px}
.nav-group-h:hover{color:var(--text-2)}
.nav-group-h .chev{flex:none;display:flex;margin-left:auto;transition:transform .18s}
.nav-acc.collapsed > .nav-group-h > .chev{transform:rotate(-90deg)}
.nav-sub{display:flex;flex-direction:column;gap:2px}
.nav-acc.collapsed > .nav-sub{display:none}
/* Sub-nivel del acordeón (Finanzas / Portal anidados): cabecera del sub-grupo indentada y algo más
   pequeña; los ítems del sub-grupo llevan una sangría extra para que se lea la jerarquía. Selectores
   acotados a hijo directo (`>`) para que colapsar el padre no arrastre el estado visual de los hijos. */
.nav-sub .nav-acc > .nav-group-h{padding-left:22px;font-size:.72rem}
.nav-sub .nav-acc > .nav-sub .nav-item{padding-left:26px}
/* AJUSTE 2 (15/07): sub-grupo con label largo (p.ej. «Liquidaciones e Informes») — la etiqueta ocupa
   el ancho disponible y ENVUELVE por palabras (sin cortar ninguna) mientras el chevron queda FIJO a la
   derecha; así el encabezado se cuadra y texto+chevron no se pisan. flex:1+min-width:0 dan a la etiqueta
   el hueco libre entre indent y chevron; line-height ajusta las 2 líneas. Los sub-headers cortos
   («Facturas») no cambian: siguen en una sola línea con el chevron a la derecha. */
.nav-sub .nav-acc > .nav-group-h > span:not(.chev){flex:1;min-width:0;text-align:left;line-height:1.2}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:8px;color:var(--text-2);font-size:14.5px;font-weight:500;text-decoration:none}
.nav-item:hover{background:var(--surface-2);text-decoration:none}
.nav-item.active{background:var(--accent-weak);color:var(--accent-text);font-weight:500}
.nav-item svg{flex:none}
/* Contraste MODO NOCHE del menú lateral principal (solo .sidebar): texto e íconos legibles sobre el
   fondo oscuro. El tema oscuro de esta app se activa SIEMPRE por [data-theme="dark"] (el JS resuelve
   "system" y fija el atributo), así que el toggle del usuario manda; por eso NO se usa
   @media(prefers-color-scheme) — activaría el color en modo día si el SO está en oscuro. El ítem
   ACTIVO se excluye con :not(.active) para conservar su acento. Los SVG heredan currentColor, así que
   fijar el color del contenedor tiñe texto e íconos por igual. DOS NIVELES de tono: (1) los ítems
   (1.er nivel + submenú .nav-sub .nav-item) van a #ededed; (2) las cabeceras-desplegable del acordeón
   (.nav-group-h) van un tono por debajo a #a3aab8 para no confundirse con los ítems. El descendiente
   .sidebar .nav-group-h cubre AMBOS niveles de cabecera: primer nivel y sub-grupos anidados
   (.nav-sub .nav-acc > .nav-group-h). Modo día: sin cambios. AJUSTE (16/07): estos dos colores son
   AHORA variables POR-USUARIO (--sb-text = ítems; --sb-text-2 = cabeceras; cookie fi_menutext,
   selector en layout.php → fiSetMenuText). SIN elección, el valor por defecto es EXACTAMENTE el de
   siempre (#ededed / #a3aab8), así que el equipo no nota ningún cambio. */
[data-theme="dark"] .sidebar .nav-item:not(.active){color:var(--sb-text,#ededed)}
[data-theme="dark"] .sidebar .nav-group-h{color:var(--sb-text-2,#a3aab8)}
.sb-foot{margin-top:auto;padding-top:10px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:8px}
.content{flex:1;min-width:0;display:flex;flex-direction:column}
.wrap{width:100%;max-width:1240px;margin:0 auto;padding:22px 24px}
/* ---- ANCHO DE PÁGINA: 1240 por defecto · «wrap-ancho» solo para rejillas y bandeja (22/07/2026) ----
   El tope de 1240px de .wrap NO es un defecto: es una regla tipográfica. Una línea de texto muy larga
   se lee peor (al saltar de renglón el ojo pierde el sitio), así que formularios, fichas y listados se
   quedan SIEMPRE en 1240. Es lo que hacen Gmail, Front e Intercom: bandeja a todo lo ancho, pantallas
   de lectura acotadas.

   ✅ CUÁNDO usar .wrap-ancho — solo si la pantalla es una REJILLA DE DATOS o una BANDEJA, es decir, si
      cada píxel de más muestra MÁS INFORMACIÓN (más días de calendario, más conversación a la vista):
        · planner.php  (el calendario: cuanto más ancho, más días caben)
        · comunicaciones.php, y SOLO en la Bandeja (pestañas bandeja | correo | conversaciones)
   🔴 CUÁNDO NO — nunca en formularios, fichas, ajustes, facturas, propietarios ni listados normales.
      Ahí el tope es lo correcto y estirarlos empeora la lectura. Si dudas: NO se pone.

   Sin tope de cordura A PROPÓSITO (max-width:none), y esto es deliberado en los DOS casos:
     · Calendario: el ancho se convierte en días visibles. Ponerle un techo sería quitarle información
       justo en el monitor grande donde más se agradece (Hostaway/Guesty tampoco lo topan).
     · Bandeja: la lista es de ancho FIJO (320/340px) y todo el sobrante va al hilo. El techo de cordura
       va donde de verdad hace falta —el TEXTO del mensaje (.cmx .cm-bub, tope en px)—, no al contenedor.
   Con esto, de 1920 a 3440 no queda hueco muerto a los lados y el texto nunca pasa de un renglón cómodo.

   Cómo se activa: la pantalla NO puede tocar el <div class="wrap"> (lo escribe lib/layout.php), así que
   se le añade la clase con una línea de JS justo al abrir el contenido. No parpadea: en ese momento el
   .wrap todavía está vacío. El padding lateral se conserva (a todo el ancho ≠ pegado al borde). */
.wrap.wrap-ancho{max-width:none}

/* ---- Barra de página (título + acciones) ---- */
.pagebar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:18px}
.crumbs{font-size:12.5px;color:var(--text-3);margin-bottom:3px}
.pagebar h1{font-size:24px}

/* ---- Botones ---- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;min-height:40px;padding:0 16px;border-radius:var(--r-md);
  border:1px solid var(--border-strong);background:var(--surface);color:var(--text);font-size:15px;font-weight:500;
  cursor:pointer;text-decoration:none;line-height:1;white-space:nowrap;transition:background .12s,border-color .12s,opacity .12s}
.btn:hover{background:var(--surface-2);text-decoration:none}
.btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--accent-weak2)}
.btn:disabled,.btn[disabled]{background:var(--surface-2)!important;border-color:var(--border)!important;color:var(--text-2)!important;cursor:not-allowed;opacity:1}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
.btn.primary:hover{background:var(--accent-strong)}
/* MARCA RentitSpain (16/07) — bajo el acento «rentit», la ACCIÓN PRINCIPAL va en el NARANJA de marca,
   no en el acento. Gramática de la marca: teal = identidad (menú activo, enlaces, foco) · naranja = LA
   única acción principal de cada pantalla. Es lo que de verdad se ve: el acento solo pinta ~2% del
   lienzo; el botón de acción salta a la vista.
   ⚠️ TEXTO OSCURO OBLIGATORIO (medido): #f5821f con blanco = 2,59:1 → ILEGIBLE. Con #14171c = 6,72:1
   (y 5,30:1 en el hover, ya oscurecido) → AA de sobra. Fondo FIJO + texto FIJO = cumple la regla de
   contraste de arriba (no se mezcla hex con token del tema), así vale igual en día y en noche.
   Solo afecta a quien elige este acento; con azul/teal/indigo/esmeralda todo sigue exactamente igual. */
[data-accent="rentit"]{--act:#f5821f;--on-act:#14171c}
[data-accent="rentit"] .btn.primary{background:var(--act);border-color:var(--act);color:var(--on-act)}
[data-accent="rentit"] .btn.primary:hover{background:color-mix(in srgb,var(--act) 86%,#000);border-color:color-mix(in srgb,var(--act) 86%,#000)}
.btn.ghost{background:transparent;border-color:transparent;color:var(--accent-text)}
.btn.ghost:hover{background:var(--accent-weak)}
.btn.danger{background:var(--danger);border-color:var(--danger);color:#fff}
.btn.danger.soft{background:transparent;color:var(--danger);border-color:color-mix(in srgb,var(--danger) 42%,var(--surface))}
.btn.danger.soft:hover{background:var(--danger-weak)}
.btn.sm{min-height:34px;font-size:14px;padding:0 12px}
.btn.lg{min-height:46px;font-size:16px;padding:0 20px}
.btn.block{width:100%}
.btn.ok{background:var(--ok-weak);border-color:color-mix(in srgb,var(--success) 34%,var(--surface));color:var(--ok-text)}
.btn.ok:hover{background:color-mix(in srgb,var(--success) 22%,var(--surface))}
.btn.warnsoft{background:var(--warn-weak);border-color:color-mix(in srgb,var(--warning) 34%,var(--surface));color:var(--warn-text)}
.btn.warnsoft:hover{background:color-mix(in srgb,var(--warning) 26%,var(--surface))}
.btn.accentsoft{background:var(--accent-weak);border-color:var(--accent-weak2);color:var(--accent-text)}
.btn.accentsoft:hover{background:var(--accent-weak2)}
.iconbtn{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border:none;background:transparent;
  color:var(--text-2);border-radius:8px;cursor:pointer;padding:0}
.iconbtn:hover{background:var(--surface-2)}
.iconbtn:disabled{opacity:.4;cursor:not-allowed}

/* ---- Cards ---- */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:18px 20px;box-shadow:var(--sh-1)}
.card+.card{margin-top:14px}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px}
.card-head h3{font-size:15px;font-weight:600}

/* ---- Formularios ---- */
label{display:block;font-size:13px;font-weight:500;color:var(--text-2);margin-bottom:5px}
input,select,textarea{width:100%;height:40px;padding:0 12px;border:1px solid var(--border-strong);border-radius:var(--r-md);
  background:var(--surface);color:var(--text);font-size:14.5px;font-family:inherit;transition:border-color .12s,box-shadow .12s}
/* Las <option> del desplegable nativo: color y fondo EXPLÍCITOS (los tokens ya cumplen AA). El
   color-scheme:dark de arriba pinta la lista oscura, pero algunos navegadores (Chrome/Windows) dejaban
   las opciones en un gris tenue heredado y no se leían («SÍ/NO» casi invisibles). 26/07 (foto de Gabriel). */
select option,select optgroup{background:var(--surface);color:var(--text)}
textarea{height:auto;min-height:90px;padding:9px 12px;line-height:1.55}
input::placeholder,textarea::placeholder{color:var(--text-3)}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-weak)}
input.inval,select.inval{border-color:var(--danger);background:var(--danger-weak)}
.field{display:flex;flex-direction:column}
.field .hint{font-size:12px;color:var(--text-3);margin-top:4px}
.field .err{font-size:12px;color:var(--danger-text);margin-top:4px}
.grid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px}

/* ---- Tablas ---- */
.tbl{width:100%;border-collapse:collapse}
.tbl th{font-size:12px;font-weight:500;text-transform:uppercase;letter-spacing:.03em;color:var(--text-3);
  text-align:left;padding:11px 12px;border-bottom:1px solid var(--border);white-space:nowrap;position:sticky;top:0;background:var(--surface)}
.tbl td{padding:12px;border-bottom:1px solid var(--border);font-size:14.5px;color:var(--text)}
.tbl tbody tr:last-child td{border-bottom:none}
.tbl tbody tr:hover{background:var(--surface-2)}
.tbl .num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.tbl .tablewrap{overflow-x:auto}

/* ---- Chips de estado ---- */
.chip{display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:500;padding:4px 11px;border-radius:var(--pill);white-space:nowrap}
.chip.ok{background:var(--ok-weak);color:var(--ok-text)}
.chip.warn{background:var(--warn-weak);color:var(--warn-text)}
.chip.danger{background:var(--danger-weak);color:var(--danger-text)}
/* 🔴 --text-2, NO --text-3 (28/07). Medido: --text-3 sobre --surface-2 da 2,77 (día) y 3,06 (noche),
   cuando el mínimo de texto es 4,5 → era el chip MENOS legible de la app, y es el que dice «Anulada».
   Con --text-2 sube a 5,43-6,76 en las 10 combinaciones (5 acentos × claro/oscuro). Solo cambia el
   COLOR DEL TEXTO: fondo, forma y tamaño quedan igual, así que el chip sigue leyéndose como el estado
   apagado frente a .ok/.warn/.danger. alta_propiedad.php:1768 ya lo parcheaba en local por esto mismo. */
.chip.muted{background:var(--surface-2);color:var(--text-2)}
.chip.accent{background:var(--accent-weak);color:var(--accent-text)}

/* ---- Alertas ---- */
.alert{border:1px solid var(--border);border-radius:var(--r-md);padding:11px 14px;font-size:13.5px;display:flex;gap:9px;align-items:flex-start;margin-bottom:14px}
.alert svg{flex:none;margin-top:1px}
.alert.ok{background:var(--ok-weak);border-color:color-mix(in srgb,var(--success) 30%,var(--surface));color:var(--ok-text)}
.alert.warn{background:var(--warn-weak);border-color:color-mix(in srgb,var(--warning) 32%,var(--surface));color:var(--warn-text)}
.alert.danger{background:var(--danger-weak);border-color:color-mix(in srgb,var(--danger) 32%,var(--surface));color:var(--danger-text)}
.alert.info{background:var(--accent-weak);border-color:var(--accent-weak2);color:var(--accent-text)}

/* ---- Métricas (resumen financiero) ---- */
.metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px}
.metric{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:15px 16px;box-shadow:var(--sh-1);position:relative}
.metric .ml{font-size:13px;font-weight:500;color:var(--text-2)}
.metric .mv{font-size:27px;font-weight:700;margin-top:5px;font-variant-numeric:tabular-nums}
.metric .md{font-size:12px;margin-top:4px;display:flex;align-items:center;gap:4px;color:var(--text-3)}
.metric .md.up{color:var(--ok-text)} .metric .md.down{color:var(--danger-text)}
.metric .mic{position:absolute;top:14px;right:14px;color:var(--text-3);opacity:.7}

/* ---- Menú desplegable (⋮) ---- */
.dd{position:relative;display:inline-block}
.ddmenu{position:absolute;right:0;top:calc(100% + 4px);min-width:186px;background:var(--panel);border:1px solid var(--border);
  border-radius:var(--r-md);box-shadow:var(--sh-2);z-index:30;display:none;overflow:hidden;padding:4px}
.ddmenu.open{display:block}
.ddmenu a,.ddmenu button{display:flex;align-items:center;gap:9px;width:100%;padding:8px 10px;border-radius:7px;font-size:13px;
  color:var(--text);text-decoration:none;background:none;border:none;cursor:pointer;text-align:left}
.ddmenu a:hover,.ddmenu button:hover{background:var(--surface-2);text-decoration:none}
.ddmenu .sep{height:1px;background:var(--border);margin:4px 0}
.ddmenu a.danger,.ddmenu button.danger{color:var(--danger-text)}

/* ---- Segmented (tema/color) ---- */
.seg{display:inline-flex;border:1px solid var(--border);border-radius:8px;overflow:hidden;background:var(--surface)}
.seg button{border:none;background:transparent;padding:6px 9px;font-size:12.5px;color:var(--text-2);cursor:pointer;display:inline-flex;align-items:center;gap:5px}
.seg button:hover{background:var(--surface-2)}
.seg button.on{background:var(--accent-weak);color:var(--accent-text)}
.swatches{display:inline-flex;gap:7px}
.swatch{width:22px;height:22px;border-radius:var(--pill);border:2px solid transparent;cursor:pointer;padding:0}
.swatch.on{border-color:var(--text)}

/* ---- Toolbar de filtros ---- */
.toolbar{display:flex;align-items:flex-end;gap:10px;flex-wrap:wrap;margin-bottom:16px}
.toolbar .field{min-width:0}
.toolbar select,.toolbar input{width:auto;min-width:150px}

/* ---- ANCLA DE FILTRO: «al filtrar te quedas donde estabas» (29/07/2026) --------------------
   REGLA DE LA CASA (memoria_referencia/regla_filtrar_sin_saltar_arriba_2907.md): una barra de
   filtros que recarga por GET deja al usuario en el scroll 0, lejos de lo que acaba de pedir.
   Se arregla con el mecanismo que el propio layout ya sanciona —el ANCLA— y no con JS: layout.php
   solo hace `scrollTo(0,0)` cuando NO hay `location.hash`, así que un `#…` en la URL manda.
   Es también lo que hace Turbo (GitHub/Basecamp) en una visita completa: «si la URL lleva ancla,
   se desplaza hasta ella; si no, al principio de la página».
   CÓMO SE USA (idéntico en todas las pantallas, una sola gramática):
     1) la barra de filtros lleva  id="xx-res" tabindex="-1" data-ancla-filtro
        aria-describedby="<id del contador de resultados>"
     2) el <form method="get"> apunta a  action="pantalla.php#xx-res"
     3) TODOS los enlaces/selects que filtran o paginan terminan en  #xx-res
     4) el contador («N facturas») lleva ese id + role="status"
   `tabindex="-1"` es lo que hace que el navegador MUEVA EL FOCO al destino del ancla (mismo
   truco que un «saltar al contenido»): así el lector de pantalla anuncia dónde ha caído y su
   descripción = cuántos resultados hay. Sin foco no se anuncia nada y se incumple WCAG 2.4.3.
   El contorno se apaga porque el contenedor NO es interactivo y no se llega a él con el tabulador
   (no aplica 2.4.7 «foco visible»); el propio salto ya es la señal visual.
   `scroll-margin-top` evita caer DEBAJO de la barra fija del móvil (.fi-tbar: 44px + notch). */
[data-ancla-filtro]{scroll-margin-top:14px}
[data-ancla-filtro]:focus{outline:none}
@media(max-width:860px){[data-ancla-filtro]{scroll-margin-top:calc(52px + env(safe-area-inset-top,0px))}}

/* ---- Estado vacío ---- */
.empty{text-align:center;padding:34px 16px;color:var(--text-3)}
.empty svg{opacity:.6;margin-bottom:10px}

/* ---- Overlay (guardando / cargando) ---- */
.overlay{position:fixed;inset:0;background:color-mix(in srgb,var(--bg) 72%,transparent);display:flex;align-items:center;justify-content:center;z-index:9999}
.overlay .box{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);padding:18px 24px;box-shadow:var(--sh-3);font-size:15px}

/* ---- Visor de factura / proforma en popup (el "ojo" de facturas2.php y proformas.php) ----
   El ojo del listado abre AQUÍ, sin cambiar de pestaña: overlay + panel con el documento
   embebido en un <iframe> → factura2_pdf.php. Un solo modal por página, reutilizado por
   todas las filas. Cierre con X, clic en el fondo o Esc; foco atrapado; scroll de detrás
   bloqueado (body.fpv-lock). El iframe va sobre BLANCO a propósito: es PAPEL (una factura),
   no una superficie del tema — igual que factura_html.php:41; no es un HEX de tema y no
   cambia con claro/oscuro. */
.fpv-ov{position:fixed;inset:0;z-index:9998;display:flex;align-items:center;justify-content:center;
  padding:24px;background:color-mix(in srgb,var(--bg) 60%,transparent)}
.fpv-ov[hidden]{display:none}
.fpv-panel{display:flex;flex-direction:column;width:min(940px,100%);height:min(90vh,100%);
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);
  box-shadow:var(--sh-3);overflow:hidden;animation:fpv-in .16s ease-out}
.fpv-head{display:flex;align-items:center;gap:10px;padding:8px 10px 8px 14px;border-bottom:1px solid var(--border)}
.fpv-tit{flex:1;min-width:0;margin:0;font-size:15px;font-weight:600;color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fpv-body{flex:1;min-height:0;background:var(--surface-2)}
.fpv-frame{width:100%;height:100%;border:0;background:#fff;display:block}
.fpv-foot{display:flex;align-items:center;gap:12px;flex-wrap:wrap;justify-content:space-between;
  padding:8px 12px;border-top:1px solid var(--border)}
.fpv-help{margin:0;font-size:12.5px;color:var(--text-3)}
.fpv-foot-acc{display:flex;gap:8px;flex-shrink:0}
@keyframes fpv-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.fpv-panel{animation:none}}
body.fpv-lock{overflow:hidden}
@media(max-width:860px){
  .fpv-ov{padding:0}
  .fpv-panel{width:100%;height:100%;max-height:none;border:0;border-radius:0;animation:none}
  .fpv-foot-acc{width:100%}
  .fpv-foot-acc .btn{flex:1;min-height:44px}
}

/* ---- Responsive ---- */
.sb-toggle{display:none}
@media(max-width:860px){
  .sidebar{position:fixed;left:0;top:0;z-index:100;transform:translateX(-100%);transition:transform .2s;box-shadow:var(--sh-3);padding-top:calc(14px + env(safe-area-inset-top));padding-bottom:calc(14px + env(safe-area-inset-bottom))}
  body.sb-open .sidebar{transform:translateX(0)}
  body.sb-open .scrim{position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:99}
  .sb-toggle{display:inline-flex}
  .wrap{padding:16px 14px}
}

/* ---- Tablas en móvil: cada fila pasa a ser una tarjeta apilada (etiqueta: valor) ---- */
.tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
@media(max-width:640px){
  .tbl thead{display:none}
  .tbl,.tbl tbody{display:block;width:100%}
  .tbl tbody tr{display:block;border:1px solid var(--border);border-radius:var(--r-md);background:var(--surface);margin:0 8px 10px;padding:2px 0}
  .tbl tbody tr:hover{background:var(--surface)}
  .tbl td{display:flex;justify-content:space-between;align-items:center;gap:14px;text-align:right;padding:9px 13px;border-bottom:1px solid var(--border);white-space:normal}
  .tbl tbody tr td:last-child{border-bottom:none}
  .tbl td::before{content:attr(data-label);font-size:11.5px;font-weight:600;text-transform:uppercase;letter-spacing:.03em;color:var(--text-3);text-align:left;flex:none;white-space:nowrap}
  .tbl td[data-label=""]::before{display:none}
  .tbl td[data-label=""]{justify-content:flex-end}
  .tbl td.num{font-variant-numeric:tabular-nums}
  .tbl td[colspan]{display:block;text-align:center}
  .tbl td[colspan]::before{display:none}
  .ddmenu{right:6px}
}

/* ---- .tbl-cards: variante para tablas ANCHAS (muchas columnas) ---- */
/* Mismo mecanismo que .tbl (cada <td> lleva data-label="Etiqueta"), pero la conversión a tarjetas
   ocurre antes, a ≤860px (el breakpoint del proyecto) → cubre el iPhone en vertical Y en horizontal
   y las tablets, no solo móviles estrechos. Se usa JUNTO a .tbl: class="tbl tbl-cards". El desktop
   (>860px) queda EXACTAMENTE igual (tabla normal). Reutilizable en cualquier listado ancho. */
@media(max-width:860px){
  .tbl-cards thead{display:none}
  .tbl-cards,.tbl-cards tbody{display:block;width:100%}
  .tbl-cards tbody tr{display:block;border:1px solid var(--border);border-radius:var(--r-md);background:var(--surface);margin:0 8px 10px;padding:2px 0}
  .tbl-cards tbody tr:hover{background:var(--surface)}
  .tbl-cards td{display:flex;justify-content:space-between;align-items:center;gap:14px;text-align:right;padding:9px 13px;border-bottom:1px solid var(--border);white-space:normal}
  .tbl-cards tbody tr td:last-child{border-bottom:none}
  .tbl-cards td::before{content:attr(data-label);font-size:11.5px;font-weight:600;text-transform:uppercase;letter-spacing:.03em;color:var(--text-3);text-align:left;flex:none;white-space:nowrap}
  .tbl-cards td[data-label=""]::before{display:none}
  .tbl-cards td[data-label=""]{justify-content:flex-end}
  .tbl-cards td.num,.tbl-cards td.r{font-variant-numeric:tabular-nums}
  .tbl-cards td[colspan]{display:block;text-align:center}
  .tbl-cards td[colspan]::before{display:none}
}

/* ---- Zona táctil cómoda en móvil (≥44px): botones, inputs, selects y menús ---- */
/* Auditoría móvil 19/07: el breakpoint era 720px pero el del proyecto es 860px → entre 721-860px
   (iPhone apaisado ~844px, tablets, plegables) los campos volvían a <16px y iOS hacía ZOOM, y los
   botones se quedaban pequeños. Alineado a 860px. Además `font-size:16px!important` para que ningún
   override de clase/inline reintroduzca el zoom (causa raíz de casi todos los hallazgos de zoom). */
@media(max-width:860px){
  .btn{min-height:44px;padding:0 18px;font-size:15px}
  .btn.sm{min-height:44px;padding:0 15px;font-size:14.5px}
  .btn.lg{min-height:48px;font-size:16px}
  .iconbtn{width:44px;height:44px}
  input,select,textarea{height:44px;font-size:16px!important}
  textarea{height:auto;min-height:96px}
  .ddmenu a,.ddmenu button{padding:11px 12px;font-size:14.5px}
}

/* ---- Progreso/porcentaje: componente ÚNICO (banner destacado + compacto .sm + estado .ok) ----
   Un solo componente para TODOS los procesos con % (corrida, subida de Excel, envío de lotes).
   Banner: arriba del contenido, número de % grande y barra alta y visible (NO enterrado al fondo). */
@keyframes fiProgSpin{to{transform:rotate(360deg)}}
.fiprog{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);padding:18px 20px;box-shadow:var(--sh-1)}
.fiprog-head{display:flex;align-items:center;gap:14px;margin-bottom:14px}
.fiprog-ic{width:40px;height:40px;flex:none;border-radius:50%;background:var(--accent-weak);color:var(--accent-text);display:flex;align-items:center;justify-content:center}
.fiprog-ic svg{animation:fiProgSpin .8s linear infinite}
.fiprog-txt{flex:1;min-width:0}
.fiprog-txt .t{font-size:15px;font-weight:600}
.fiprog-txt .s{font-size:13px;color:var(--text-2);margin-top:2px}
.fiprog-pct{font-size:34px;font-weight:700;color:var(--accent-text);font-variant-numeric:tabular-nums;line-height:1}
.fiprog-pct span{font-size:20px}
.fiprog-bar{height:14px;background:var(--accent-weak);border-radius:var(--pill);overflow:hidden}
.fiprog-bar>i{display:block;height:100%;width:0;background:var(--accent);border-radius:var(--pill);transition:width .25s ease}
/* Compacto (dentro de tarjeta / subida de archivo) */
.fiprog.sm{padding:14px 16px;box-shadow:none}
.fiprog.sm .fiprog-head{margin-bottom:8px}
.fiprog.sm .fiprog-bar{height:10px}
.fiprog.sm .fiprog-pct{font-size:18px}
.fiprog.sm .fiprog-pct span{font-size:13px}
.fiprog.sm .fiprog-ic{display:none}
/* Estado final OK (reusa .fiprog, cambia el acento a éxito) */
.fiprog.ok .fiprog-ic{background:var(--ok-weak);color:var(--ok-text)}
.fiprog.ok .fiprog-ic svg{animation:none}
.fiprog.ok .fiprog-pct{color:var(--ok-text)}
.fiprog.ok .fiprog-bar>i{background:var(--success)}

/* ---- Documento sobre lienzo blanco (previsualización de correo/factura/liquidación). ----
   SIEMPRE claro y legible, no depende del tema: así el cliente lo ve tal cual. */
.paper{background:#ffffff;color:#1f2430;border:1px solid #e3e6ec;border-radius:var(--r-md)}
.paper h1,.paper h2,.paper h3,.paper h4{color:#1a1f29}
.paper .p-label{color:#8a93a3}
.paper .p-soft{color:#5c6472}
.paper .p-rule{border-color:#e6e9ef}
.paper a{color:#2563eb}

/* ==========================================================================
   WIZARD DE ALTA / formularios por pasos — añadido 17/07/2026 (interfaz).
   ADITIVO PURO: 0 líneas borradas, ninguna clase existente redefinida. Estas
   clases NO existían en el design system; salen de la maqueta aprobada por
   Gabriel el 17/07 y están construidas SOLO con tokens ya vivos (--accent,
   --surface, --border-strong, --pill…), así que heredan la piel de marca y el
   modo oscuro sin tocar nada. Las usa alta_propiedad.php (wizard del
   propietario) y son reutilizables por cualquier formulario por pasos.
   Densidad: los objetivos táctiles se quedan a 44-48px SIEMPRE (el propietario
   rellena esto con el dedo, en el móvil) — no se les aplica la densidad de
   escritorio a propósito.
   --------------------------------------------------------------------------
   ESCALA PARAMETRIZADA — 21/07/2026 («letra grande» del alta de propiedad).
   Los tamaños de letra, los altos táctiles, las separaciones y el grosor de
   borde de ESTE bloque salen ahora de variables `--wz-*` **con fallback al
   valor de siempre**:
     · Quien NO las define (altas_admin.php, y cualquier pantalla futura que
       reutilice .opt/.chipopt) se queda EXACTAMENTE igual que hoy, al píxel.
     · Quien SÍ las define en su raíz (alta_propiedad.php: propietarios mayores
       rellenando en el móvil) sube TODA la escala cambiando una sola variable.
   Cero HEX nuevos: todo sigue saliendo de los tokens del sistema, así que vale
   igual en tema claro y oscuro y con los cinco acentos elegibles.

   🔴 FOCO VISIBLE (misma fecha): el foco era `box-shadow 0 0 0 3px
   var(--accent-weak2)`. --accent-weak2 es el acento al 26 % mezclado con
   --surface → sobre la propia tarjeta da ~1,5:1, o sea INVISIBLE para quien
   navega con teclado (y para cualquiera con poca vista). Se sustituye por un
   contorno SÓLIDO de 3px en var(--text) separado 2px: no usa el acento, así que
   contrasta igual con los cinco acentos y en los dos temas (≈17:1 en claro,
   ≈14:1 en oscuro). Es corrección de accesibilidad, no de estética: se aplica a
   todas las pantallas que usen estas clases.
   ========================================================================== */

/* Opciones grandes tocables: sí/no, listas y tarjetas de elección. */
.opts{display:flex;flex-direction:column;gap:var(--wz-gap,8px)}
.opts.row{flex-direction:row;flex-wrap:wrap}
.opt{display:flex;align-items:center;gap:10px;min-height:var(--wz-tap,48px);padding:var(--wz-pad,10px 14px);width:100%;text-align:left;
  border:var(--wz-bd,1px) solid var(--border-strong);border-radius:var(--r-md);background:var(--surface);color:var(--text);
  font-size:var(--wz-fs,15px);font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s}
.opt:hover{background:var(--surface-2)}
.opt:focus-visible,.opt:focus-within{outline:3px solid var(--text);outline-offset:2px;box-shadow:none}
/* Seleccionado. Va por partida doble a propósito: `.on` lo pone el JS (respuesta
   instantánea al dedo) y `:has(input:checked)` lo pone el navegador solo → si el
   JS no llega a cargar, el propietario SIGUE viendo qué ha elegido. */
.opt.on,.opt:has(input:checked){background:var(--accent-weak);border-color:var(--accent);color:var(--accent-text);font-weight:600}
.opt .tick{margin-left:auto;opacity:0;flex:none}
.opt.on .tick,.opt:has(input:checked) .tick{opacity:1}
.opt .d{display:block;font-size:var(--wz-fs-sec,12.5px);font-weight:400;color:var(--wz-help,var(--text-3));margin-top:2px}
.opts.row .opt{width:auto;min-height:var(--wz-tap-row,44px);flex:1 1 90px;justify-content:center;text-align:center}

/* Opciones en pastilla: para listas largas (meses, tamaños de cama…). */
.chipopts{display:flex;flex-wrap:wrap;gap:var(--wz-gap-chip,7px)}
.chipopt{min-height:var(--wz-tap-chip,38px);padding:0 var(--wz-pad-chip,13px);border-radius:var(--pill);border:var(--wz-bd,1px) solid var(--border-strong);
  background:var(--surface);color:var(--text-2);font-size:var(--wz-fs-chip,14px);font-family:inherit;cursor:pointer}
.chipopt:focus-visible,.chipopt:focus-within{outline:3px solid var(--text);outline-offset:2px;box-shadow:none}
.chipopt.on,.chipopt:has(input:checked){background:var(--accent-weak);border-color:var(--accent);color:var(--accent-text);font-weight:600}
/* El radio/checkbox real vive dentro de la pastilla, invisible pero presente: es
   él quien guarda la respuesta y quien la hace accesible con el teclado. */
.opt input.opt-in,.chipopt input.opt-in{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.opt,.chipopt{position:relative}
.chipopt{display:inline-flex;align-items:center;justify-content:center}

/* Barra de progreso del wizard. */
.prog{margin-bottom:16px}
.prog .lbl{display:flex;justify-content:space-between;gap:10px;font-size:var(--wz-fs-sec,12px);color:var(--wz-help,var(--text-3));margin-bottom:6px}
.prog .bar{height:var(--wz-bar,6px);border-radius:var(--pill);background:var(--surface-2);overflow:hidden}
.prog .bar i{display:block;height:100%;background:var(--accent);border-radius:var(--pill);transition:width .25s}

/* Campo con botón de micrófono = dictado NATIVO del móvil (coste 0, sin API). */
.mic-wrap{display:flex;gap:8px;align-items:flex-start}
.mic-wrap textarea,.mic-wrap input{flex:1;min-width:0}
.mic-hint{font-size:var(--wz-fs-sec,12px);color:var(--wz-help,var(--text-3));margin-top:4px}
.iconbtn.rec{background:var(--danger-weak);border-color:var(--danger);color:var(--danger-text)}

/* Barra de acciones inferior (a la altura del pulgar) + casilla de consentimiento. */
.wz-actions{display:flex;gap:var(--wz-gap,8px);margin-top:18px}
/* Sin min-height aquí a propósito: `.wz-actions .btn` (0,2,0) le ganaría a la regla
   móvil `.btn{min-height:44px}` (0,1,0) de más arriba y encogería los botones en el
   móvil de cualquier pantalla que no defina --wz-tap. El alto lo fija el consumidor. */
.wz-actions .btn{flex:1}
.wz-luego{text-align:center;margin-top:10px}
.wz-check{display:flex;align-items:flex-start;gap:9px;font-size:var(--wz-fs,14px);color:var(--text);cursor:pointer;margin:12px 0}
.wz-check input{width:var(--wz-box,20px);height:var(--wz-box,20px);flex:none;margin-top:1px}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   DENSIDAD EN MÓVIL — «que cuadre y ocupe el mayor espacio posible» (Gabriel 21/07/2026)
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   Encargo textual: «intenta cuadrar más la pantalla para que ocupe el mayor espacio posible y quede
   mejor repartido». Va AL FINAL del fichero y SOLO dentro de media queries de móvil, a propósito:
   así es puramente aditivo (no reescribe ninguna regla de arriba) y el ESCRITORIO no cambia ni un
   píxel — cualquiera puede borrar este bloque entero y la app vuelve exactamente a como estaba.

   Qué se recorta y por qué (todo medido a 390 px de ancho, el iPhone de referencia):
     · .wrap  22/24 → 16/14 (ya estaba) → 12/12: el aire de arriba se nota vacío cuando encima hay
       una barra fija; y 12 px de margen lateral es lo que usan iOS (16 pt) y Material (16 dp) en su
       versión compacta. Gana 4 px de alto y 4 px de ancho útil por lado.
     · .pagebar margin-bottom 18 → 12 px: el hueco entre el título y el contenido era de escritorio.
     · h1 25 → 22 px y .pagebar h1 24 → 21 px SOLO en ≤640 px: los tamaños de PC dejan el título en
       dos líneas en cuanto es un poco largo, y dos líneas cuestan 30 px. Sigue muy por encima del
       mínimo legible y mantiene la jerarquía con h2 (20 px).
     · .crumbs margin-bottom 3 → 2 px.
   Total recuperado arriba en una pantalla con título: ~10 px, además de los de la barra superior.

   🔴 NO se toca ningún tamaño de campo: inputs/selects/textarea siguen en 16 px (por debajo, iOS
   hace zoom al enfocar) y los toques siguen en 44 px. Esto solo quita AIRE, nunca zona táctil. */
@media(max-width:860px){
  .wrap{padding:12px 12px}
  .pagebar{margin-bottom:12px}
  .crumbs{margin-bottom:2px}
}
@media(max-width:640px){
  h1{font-size:22px}
  .pagebar h1{font-size:21px}
  /* Tablas convertidas en tarjetas: llevaban 8 px de margen lateral PROPIO que se sumaban a los del
     .wrap → las tarjetas quedaban más estrechas que el resto del contenido y se veía desalineado
     («mejor repartido», dijo Gabriel). Se alinean con la columna; el hueco entre tarjetas se queda. */
  .tbl tbody tr{margin-left:0;margin-right:0}
}
@media(max-width:860px){
  .tbl-cards tbody tr{margin-left:0;margin-right:0}
}

/* ---- «Tirar para actualizar» (pull-to-refresh) — indicador flotante en NARANJA de marca (var(--act)).
   Ver assets/pull-refresh.js. Spinner tipo «cometa» (conic-gradient enmascarado); el JS lo gira al tirar
   y en bucle al soltar. Todo por tokens (claro/oscuro + pieles). pointer-events:none = nunca estorba. */
@keyframes ptr-spin{to{transform:rotate(360deg)}}
@keyframes ptr-pop{0%{transform:scale(.55)}60%{transform:scale(1.14)}100%{transform:scale(1)}}
.ptr{position:fixed;left:0;right:0;top:calc(env(safe-area-inset-top,0px) + 8px);
  display:flex;justify-content:center;pointer-events:none;z-index:70}
.ptr-pill{width:46px;height:46px;border-radius:50%;background:var(--surface);
  border:1px solid var(--border);box-shadow:var(--sh-2);display:flex;align-items:center;
  justify-content:center;transform:translateY(-76px);opacity:0;will-change:transform,opacity}
.ptr-spin{width:26px;height:26px;border-radius:50%;
  background:conic-gradient(from 0deg, color-mix(in srgb, var(--act, var(--warning)) 6%, transparent), var(--act, var(--warning)));
  -webkit-mask:radial-gradient(farthest-side, transparent calc(100% - 3.5px), #000 0);
          mask:radial-gradient(farthest-side, transparent calc(100% - 3.5px), #000 0)}
.ptr-ready .ptr-pill{border-color:var(--act, var(--warning));
  box-shadow:0 6px 18px color-mix(in srgb, var(--act, var(--warning)) 48%, transparent), var(--sh-2);
  animation:ptr-pop .32s cubic-bezier(.2,.7,.3,1)}
.ptr-spin.go{animation:ptr-spin .66s linear infinite}
.ptr-anim .ptr-pill{transition:transform .28s cubic-bezier(.2,.7,.3,1),opacity .28s ease}
.ptr-reduce.ptr-anim .ptr-pill{transition:transform .2s linear,opacity .2s linear}
.ptr-reduce.ptr-ready .ptr-pill{animation:none}
.ptr-reduce .ptr-spin.go{animation-duration:1.3s}
.ptr-live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* TELÉFONO/CORREO ACCIONABLE — «píldora» GLOBAL (29/07/2026).
   Nació inline en la Bandeja (comunicaciones.php); se sube aquí para que tel_render_html()
   (lib/telefonos.php) se vea IGUAL en cualquier pantalla: ficha de cliente, check-in, etc.
   Solo tokens → claro y oscuro salen solos. Móvil: número ≥16px (iOS no hace zoom al enfocar) y
   botón 44px (toque). El .tel-copy lo copia un ÚNICO manejador delegado del pie (lib/layout.php). */
.tel-pill{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap;vertical-align:middle}
.tel-num{color:var(--accent);text-decoration:none;font-weight:600}
.tel-num:hover{text-decoration:underline}
.tel-copy{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;font-size:12px;line-height:1;background:var(--surface-2);border:1px solid var(--border);border-radius:var(--r-sm);color:var(--text-2);cursor:pointer;flex:none}
.tel-copy:hover{background:color-mix(in srgb,var(--accent) 12%,var(--surface-2));border-color:color-mix(in srgb,var(--accent) 35%,var(--border));color:var(--accent)}
.tel-copy.ok{color:var(--success);border-color:color-mix(in srgb,var(--success) 45%,var(--border))}
@media(max-width:640px){.tel-num{font-size:16px}.tel-copy{width:44px;height:44px;font-size:16px}}
