/* ============================================================================
 * TES · Capa de diseño fluido (a la manera de Apple)
 *
 * Todo el movimiento vive en este archivo y en `js/fluido.js`, que
 * se cargan DESPUÉS de `styles.css` y lo afinan sin reescribirlo. Para volver
 * a la versión anterior basta con quitar las dos líneas que los cargan en
 * `index.html` (y el enganche de transiciones del router en `app.js`).
 *
 * Lo que NO cambia, por ser la marca: los colores (tokens de `:root`), la
 * tipografía IBM Plex Sans y sus dos únicos pesos. (Los radios y los
 * epígrafes los cambia la capa del aspecto: diseno-apple.css.)
 *
 * Lo que aporta, siguiendo las pautas de diseño de Apple (WWDC, «Designing
 * Fluid Interfaces»):
 *   1. Movimiento con MUELLES, no con duraciones fijas: sin rebote por
 *      defecto y un rebote leve solo cuando el gesto lleva impulso.
 *   2. RESPUESTA inmediata: todo lo que se pulsa reacciona al apoyar el dedo,
 *      no al levantarlo.
 *   3. MATERIALES translúcidos en las barras que flotan sobre el contenido:
 *      sin fondo en reposo, cristal cuando el contenido pasa por debajo, y un
 *      desvanecido en vez de una raya («scroll edge effect»). La barra de
 *      pestañas del móvil flota como una cápsula.
 *   4. CONSISTENCIA ESPACIAL: los menús nacen de su botón, las hojas suben de
 *      abajo y se van por donde vinieron, al entrar en algo la pantalla
 *      avanza y al volver retrocede, «volver» vive siempre arriba a la
 *      izquierda y la píldora de un control segmentado viaja de una opción a
 *      otra en vez de saltar.
 *   5. MANIPULACIÓN DIRECTA: las filas de las listas se deslizan con el dedo
 *      para dejar ver «Eliminar», como en iOS.
 *   6. TIPOGRAFÍA con el espaciado según el tamaño: los titulares grandes se
 *      aprietan, el texto corriente queda como está.
 *   7. ACCESIBILIDAD: quien pide menos movimiento, menos transparencia o más
 *      contraste lo tiene.
 * ========================================================================== */

/* ---- 1. Muelles -----------------------------------------------------------
 * Calculados a partir de los dos parámetros con los que Apple describe un
 * muelle —amortiguación y respuesta— y convertidos a una curva `linear()`.
 * La duración es la que tarda en quedarse a menos del 0,1 % del destino: un
 * muelle no tiene duración propia, le sale de sus parámetros.
 *   muelle         amortiguación 1,0 · respuesta 0,35 s  (por defecto)
 *   muelle-rapido  amortiguación 1,0 · respuesta 0,25 s  (menús, pulsaciones)
 *   muelle-hoja    amortiguación 0,8 · respuesta 0,30 s  (hojas con impulso)
 * -------------------------------------------------------------------------- */
:root {
  --muelle-dur: 520ms;
  --muelle: linear(0, 0.0233, 0.0803, 0.1558, 0.2398, 0.3254, 0.4083, 0.4859, 0.5568, 0.6205, 0.6769, 0.7262, 0.769, 0.8059, 0.8374, 0.8642, 0.8869, 0.906, 0.9221, 0.9355, 0.9468, 0.9561, 0.9639, 0.9703, 0.9756, 0.98, 0.9836, 0.9866, 0.9891, 0.9911, 0.9927, 0.9941, 0.9952, 0.9961, 0.9968, 0.9974, 0.9979, 0.9983, 0.9986, 0.9989, 1);
  --muelle-rapido-dur: 370ms;
  --muelle-rapido: linear(0, 0.0232, 0.0798, 0.1549, 0.2385, 0.3237, 0.4064, 0.4839, 0.5547, 0.6184, 0.6748, 0.7243, 0.7672, 0.8041, 0.8358, 0.8627, 0.8856, 0.9049, 0.921, 0.9346, 0.946, 0.9554, 0.9633, 0.9698, 0.9752, 0.9796, 0.9833, 0.9863, 0.9888, 0.9909, 0.9925, 0.9939, 0.995, 0.996, 0.9967, 0.9973, 0.9978, 0.9982, 0.9986, 0.9988, 1);
  --muelle-hoja-dur: 410ms;
  --muelle-hoja: linear(0, 0.0205, 0.0732, 0.1465, 0.2317, 0.322, 0.4123, 0.4993, 0.5804, 0.6542, 0.72, 0.7776, 0.8271, 0.8689, 0.9036, 0.932, 0.9548, 0.9727, 0.9865, 0.9968, 1.0042, 1.0093, 1.0125, 1.0144, 1.0151, 1.015, 1.0144, 1.0134, 1.0122, 1.0108, 1.0094, 1.0081, 1.0068, 1.0057, 1.0046, 1.0037, 1.0029, 1.0022, 1.0017, 1.0012, 1);
  /* Salida: rápida y acelerando, espejo de la entrada. Lo que se va no
     necesita posarse, solo quitarse de en medio. */
  --salida: cubic-bezier(0.4, 0, 1, 1);
  --salida-dur: 220ms;

  /* Sombras en capas —una corta que asienta, otra larga que eleva—, en el
     mismo tono cálido de la tinta de marca y tan tenues como pide el manual. */
  --shadow-sm: 0 0.5px 1px rgba(48, 41, 41, 0.05), 0 1px 3px rgba(48, 41, 41, 0.04);
  --shadow-md: 0 1px 2px rgba(48, 41, 41, 0.05), 0 6px 18px rgba(48, 41, 41, 0.07);
  --shadow-lg: 0 2px 6px rgba(48, 41, 41, 0.06), 0 24px 60px rgba(48, 41, 41, 0.18);

  /* Materiales: fondo translúcido + desenfoque + saturación, como el cristal
     de las barras de iOS. El tono sale del blanco y del fondo de la app. */
  --material-claro: rgba(255, 255, 255, 0.72);
  /* El de las barras de arriba, más tupido: deja intuir lo que pasa por
     debajo, pero sin que un botón naranja se convierta en una mancha. */
  --material-fondo: rgba(244, 243, 239, 0.84);
  --material-menu: rgba(255, 255, 255, 0.84);
  --material-filtro: saturate(180%) blur(20px);
  --filete: rgba(48, 41, 41, 0.1);          /* línea de medio píxel */

  /* Pulsación: velo que oscurece lo pulsado sin tocar su color */
  --pulsado: rgba(48, 41, 41, 0.055);
}
/* Navegadores sin `linear()` (Safari anterior a 17.2): una curva de salida
   suave que se le parece. Mejor eso que un movimiento lineal y seco. */
@supports not (transition-timing-function: linear(0, 1)) {
  :root {
    --muelle: cubic-bezier(0.22, 1, 0.36, 1);
    --muelle-rapido: cubic-bezier(0.22, 1, 0.36, 1);
    --muelle-hoja: cubic-bezier(0.3, 1.25, 0.4, 1);
  }
}

/* ---- 2. Respuesta inmediata ---------------------------------------------- */
/* Sin el destello gris de iOS al tocar ni la espera de 300 ms del doble toque:
   la respuesta la damos nosotros, y al instante. */
html { -webkit-tap-highlight-color: transparent; }
a, button, input, select, textarea, label, [role="button"], [tabindex] { touch-action: manipulation; }

/* Controles compactos: se hunden al apoyar el dedo (100 ms, sin esperar al
   clic) y vuelven con un muelle al soltar. */
:is(.btn, .campana, .topbar-cuenta, .badge-btn, .btn-sm-danger, .edit-btn,
    .cf-filtro-op, .cf-modo-op, .cf-mas, .cf-mes-quitar, .cfgg-plegar,
    .cfx3-serie, .cli-tipo-op, .crm-card-btn, .crm-etapa-op, .filtro-chip, .filtro-op,
    .seg-tab, .tab-seccion, .serv-add-btn, .serv-sug-btn, .serv-chip-x, .perfil-add,
    .combo-add, .row-accion, .row-delete, .doc-na-btn, .otros-accion, .panel-btn-gan,
    .panel-crear-btn, .ficha-cerrar, .link-mini, .cert-reabrir, .perfil-item-accion,
    .dash-periodo) {
  transition-property: transform, background-color, border-color, color, box-shadow, opacity, filter;
  transition-duration: var(--muelle-rapido-dur), 0.15s, 0.15s, 0.15s, 0.2s, 0.15s, 0.15s;
  transition-timing-function: var(--muelle-rapido), ease, ease, ease, ease, ease, ease;
}
:is(.btn, .campana, .topbar-cuenta, .badge-btn, .btn-sm-danger, .edit-btn,
    .cf-filtro-op, .cf-modo-op, .cf-mas, .cf-mes-quitar, .cfgg-plegar,
    .cfx3-serie, .cli-tipo-op, .crm-card-btn, .crm-etapa-op, .filtro-chip, .filtro-op,
    .seg-tab, .tab-seccion, .serv-add-btn, .serv-sug-btn, .serv-chip-x, .perfil-add,
    .combo-add, .row-accion, .row-delete, .doc-na-btn, .otros-accion, .panel-btn-gan,
    .panel-crear-btn, .ficha-cerrar, .link-mini, .cert-reabrir, .perfil-item-accion):active:not(:disabled) {
  transform: scale(0.96);
  transition-duration: 100ms;
  transition-timing-function: ease-out;
}
/* Los botones grandes se hunden menos: un 4 % de 360 px ya son 14 px. */
.btn:active:not(:disabled) { transform: scale(0.975); }

/* Tarjetas que se abren: al pasar el ratón se elevan un poco (solo con
   ratón: en el móvil no hay «pasar por encima»), y al pulsar se asientan. */
:is(.acceso-card, .cert-card, .doc-card, .modo-card, .proy-card, .adic-opcion,
    .cfg-sinplazo-item, .mapa-pendiente, .crm-card) {
  transition-property: transform, box-shadow, border-color, background-color;
  transition-duration: var(--muelle-dur), var(--muelle-dur), 0.15s, 0.15s;
  transition-timing-function: var(--muelle), var(--muelle), ease, ease;
}
@media (hover: hover) and (pointer: fine) {
  :is(.acceso-card, .cert-card, .doc-card, .modo-card, .proy-card, .adic-opcion):hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
  }
}
:is(.acceso-card, .cert-card, .doc-card, .modo-card, .proy-card, .adic-opcion,
    .cfg-sinplazo-item, .mapa-pendiente, .crm-card):active {
  transform: scale(0.985);
  transition-duration: 100ms;
  transition-timing-function: ease-out;
}

/* Filas y navegación: al pulsar se ilumina la fila entera al instante, sin
   encogerla (una fila de lista que menguase haría bailar a sus vecinas). El
   velo va por encima de su fondo, sea el que sea, y por debajo del texto. */
:is(.nav-item, .nav-subitem, .tab-item, .menu-flot-item, .estado-menu-item,
    .fase-menu-item, .combo-item, .perfil-item, .row-obra, .row-cli, .row-crm,
    .mini-row, .cert-hist-row, .doc-fila, .cc-action, .panel-via, .panel-etapa,
    .cli-via, .sb-usuario, .sb-alert, .link-back, .perfil-goto):active {
  box-shadow: inset 0 0 0 100vmax var(--pulsado);
  transition: none;
}

/* ---- 3. Barras: cristal que aparece al desplazar, sin rayas --------------- *
 * Como las barras de iOS y macOS actuales:
 *   · En reposo la barra no tiene fondo: no hay nada debajo que separar.
 *   · En cuanto el contenido pasa por debajo (`html.desplazado`, fluido.js)
 *     aparece el cristal y, en vez de un filete, un DESVANECIDO: el contenido
 *     se funde al llegar a la barra («scroll edge effect»).
 *   · El título grande, al quedar debajo de la barra, aparece en ella en
 *     pequeño (`html.titulo-en-barra`, fluido.js).
 * El aspecto de sus botones (círculos y cápsulas) va en diseno-apple.css.
 * -------------------------------------------------------------------------- */
.main-bar { background: transparent; transition: background-color 0.25s ease; }
html.desplazado .main-bar {
  background: var(--material-fondo);
  -webkit-backdrop-filter: var(--material-filtro);
  backdrop-filter: var(--material-filtro);
}
/* El desvanecido: una franja bajo la barra que va del tono del cristal a nada. */
.main-bar::after, .topbar::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 18px;
  background: linear-gradient(var(--material-fondo), rgba(244, 243, 239, 0));
  opacity: 0; transition: opacity 0.25s ease; pointer-events: none;
}
html.desplazado .main-bar::after, html.desplazado .topbar::after { opacity: 1; }

/* Lo que se queda fijo al desplazar (la cabecera del cashflow) se queda
   DEBAJO de la barra, no detrás: si no, se vería borroso a través del cristal.
   `--alto-barra` es lo que mide la franja de arriba en tableta y escritorio. */
@media (min-width: 761px) {
  :root { --alto-barra: 54px; }
  .cf-filtro-fijo { top: var(--alto-barra); }
  .cfgg-sticky-top { top: calc(var(--alto-barra) + var(--cf-alto-filtro, 0px)); }
}

/* El título de la pantalla, en pequeño, dentro de la barra. */
.barra-titulo {
  min-width: 0;
  font-size: 15px; font-weight: 600; letter-spacing: -0.012em; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  opacity: 0; transform: translateY(5px); pointer-events: none;
  transition: opacity 0.18s ease, transform var(--muelle-rapido-dur) var(--muelle-rapido);
}
html.titulo-en-barra .barra-titulo { opacity: 1; transform: none; }
.main-bar .barra-titulo { flex: 1 1 auto; padding: 0 14px; }

/* Barra lateral: la región estructural, de material más pesado (opaco) que
   las barras flotantes. El filete, de medio píxel. */
.sidebar { border-right: 0; box-shadow: 0.5px 0 0 var(--filete); }
.sb-brand { border-bottom-color: transparent; box-shadow: 0 0.5px 0 var(--filete); }
.nav-item, .nav-subitem { transition: background-color 0.15s ease, color 0.15s ease; }
/* El filete naranja de la sección activa crece desde el centro al llegar. */
.nav-item.active::before { animation: filete-crece var(--muelle-dur) var(--muelle) both; }
@keyframes filete-crece { from { transform: scaleY(0.3); opacity: 0; } to { transform: none; opacity: 1; } }

@media (max-width: 760px) {
  /* Lo que ocupa desde abajo la barra de pestañas flotante: su alto (62) y
     lo que flota sobre el borde, que respeta la zona del indicador de inicio. */
  :root { --tabbar-flota: calc(62px + max(10px, calc(var(--safe-b) - 10px))); }
  /* Cabecera del móvil: la misma idea. En reposo se funde con la página; al
     desplazar, cristal del tono del fondo y el desvanecido debajo. */
  .topbar { background: transparent; border-bottom: 0; transition: background-color 0.25s ease; }
  html.desplazado .topbar {
    background: var(--material-fondo);
    -webkit-backdrop-filter: var(--material-filtro);
    backdrop-filter: var(--material-filtro);
  }
  /* El título, centrado en la cabecera, como en la barra de navegación de
     iOS; con sitio a los lados para la marca (o volver) y los botones. */
  .topbar .barra-titulo {
    position: absolute; left: 50%; top: var(--safe-t);
    max-width: calc(100% - 236px);
    height: var(--topbar-h); line-height: var(--topbar-h);
    transform: translate(-50%, 5px);
  }
  html.titulo-en-barra .topbar .barra-titulo { transform: translate(-50%, 0); }

  /* La barra de pestañas flota (su forma, en diseno-apple.css): cristal más
     claro y más opaco que el de la cabecera, porque es un control al que se
     llama la atención, no un fondo. */
  .tabbar {
    background: rgba(255, 255, 255, 0.78);
    -webkit-backdrop-filter: saturate(180%) blur(24px);
    backdrop-filter: saturate(180%) blur(24px);
    border-top: 0;
  }
  /* Y al pie, el mismo desvanecido que arriba: el contenido se funde antes
     de pasar por debajo de la barra flotante. */
  .app-shell::after {
    content: ''; position: fixed; left: 0; right: 0; bottom: 0; z-index: 44;
    height: calc(var(--tabbar-flota) + 30px);
    background: linear-gradient(rgba(244, 243, 239, 0), rgba(244, 243, 239, 0.9) 72%);
    pointer-events: none;
  }
  /* El icono de la pestaña activa da un pequeño salto de escala al llegar:
     confirma el cambio sin distraer. */
  .tab-item.active .nav-ic { animation: pestana-llega var(--muelle-dur) var(--muelle-hoja) both; }
  .tab-item { transition: color 0.15s ease, background-color 0.2s ease; }
}
@keyframes pestana-llega { from { transform: scale(0.82); } to { transform: none; } }

/* ---- 4. Tipografía: espaciado según el tamaño ---------------------------- *
 * Lo grande se aprieta (las letras de un titular, al crecer, parecen
 * separarse); lo pequeño en mayúsculas mantiene el 0,13em de la marca. Los
 * pesos siguen siendo los dos de siempre: la jerarquía sale de tamaño, peso e
 * interlineado a la vez.
 * -------------------------------------------------------------------------- */
.page-title { letter-spacing: -0.022em; line-height: 1.12; }
.empty h2, .modal-title, .modal-datos > .modal-title { letter-spacing: -0.018em; line-height: 1.2; }
:is(.kpi-value, .cf-cifra-val, .cfx3-hero-val, .cfg-total-val) {
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.page-sub { line-height: 1.45; }

/* ---- 5. Menús: nacen de su botón ------------------------------------------ *
 * Se materializan —escala, opacidad y desenfoque a la vez— desde el punto
 * donde está el botón que los abre, y son de cristal: se intuye lo que
 * queda debajo sin que estorbe.
 * -------------------------------------------------------------------------- */
:is(.menu-flot, .estado-menu, .fase-menu, .combo-panel, .perfil-panel) {
  background: var(--material-menu);
  -webkit-backdrop-filter: var(--material-filtro);
  backdrop-filter: var(--material-filtro);
  border-color: rgba(48, 41, 41, 0.08);
  box-shadow: var(--shadow-lg);
}
:is(.estado-menu.open, .fase-menu.open, .combo-panel.open, .perfil-panel.open, .menu-ancla.abierto .menu-flot) {
  animation: materializa var(--muelle-rapido-dur) var(--muelle-rapido) both;
}
.estado-menu, .fase-menu, .combo-panel, .perfil-panel { transform-origin: top left; }
.menu-flot { transform-origin: top left; }
.menu-arriba { transform-origin: bottom left; }
.menu-abajo { transform-origin: top right; }
@keyframes materializa {
  from { opacity: 0; transform: scale(0.94); filter: blur(3px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* ---- 6. Diálogos y hojas -------------------------------------------------- *
 * Escritorio y tableta: el diálogo se materializa en el centro, y el fondo se
 * atenúa para centrar la atención. Al cerrarse (fluido.js le pone
 * `.saliendo`) hace el camino inverso, más deprisa.
 * -------------------------------------------------------------------------- */
.modal-overlay {
  background: rgba(30, 24, 24, 0.3);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  animation: velo-entra var(--muelle-dur) var(--muelle) both;
}
.modal-overlay > .modal {
  animation: dialogo-entra var(--muelle-dur) var(--muelle) both;
  will-change: transform, opacity;
}
.modal-overlay.saliendo { pointer-events: none; animation: velo-sale var(--salida-dur) var(--salida) both; }
.modal-overlay.saliendo > .modal { animation: dialogo-sale var(--salida-dur) var(--salida) both; }

@keyframes velo-entra { from { opacity: 0; } to { opacity: 1; } }
@keyframes velo-sale  { from { opacity: 1; } to { opacity: 0; } }
@keyframes dialogo-entra {
  from { opacity: 0; transform: scale(0.95); filter: blur(4px); }
  to   { opacity: 1; transform: none; filter: none; }
}
@keyframes dialogo-sale {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: scale(0.97); }
}

/* Móvil: el diálogo es una HOJA que sube desde abajo, con su asa, y se va
   hacia abajo, por donde vino. Se puede arrastrar hacia abajo para cerrarla
   (fluido.js). */
@media (max-width: 760px) {
  /* Como en iOS: detrás de una hoja el contenido solo se atenúa, no se
     desenfoca; se sigue intuyendo dónde se estaba. */
  .modal-overlay {
    background: rgba(30, 24, 24, 0.34);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    place-items: end stretch;
    padding: max(28px, calc(var(--safe-t) + 12px)) 0 0;
  }
  .modal-overlay > .modal {
    position: relative;
    width: 100%; max-width: none;
    margin: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    max-height: calc(100dvh - max(28px, calc(var(--safe-t) + 12px)));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-top: 30px;
    padding-bottom: calc(20px + var(--safe-b));
    box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.6), var(--shadow-lg);
    animation: hoja-sube var(--muelle-dur) var(--muelle) both;
  }
  /* Los diálogos con cabecera y pie propios (perfil, datos de la obra…)
     llevan su propio relleno: solo se les deja sitio para el asa arriba. */
  .modal-overlay > .modal:is(.modal-perfil, .modal-datos, .modal-adic, .modal-archivos) {
    padding-top: 14px;
    padding-bottom: var(--safe-b);
  }
  /* El asa: dice sin palabras que la hoja se puede bajar. */
  .modal-overlay > .modal::before {
    content: '';
    position: absolute; top: 8px; left: 50%;
    width: 38px; height: 5px; margin-left: -19px;
    border-radius: 3px;
    background: var(--border-strong);
  }
  .modal-overlay.saliendo > .modal { animation: hoja-baja var(--salida-dur) var(--salida) both; }
  /* Mientras se arrastra, manda el dedo: nada de animaciones por medio. */
  .modal-overlay > .modal.arrastrando { animation: none; transition: none; user-select: none; }
}
@keyframes hoja-sube { from { transform: translateY(100%); } to { transform: none; } }
@keyframes hoja-baja { from { transform: var(--desde, none); } to { transform: translateY(100%); } }

/* ---- 7. Transiciones entre pantallas ------------------------------------- *
 * Con la API de transiciones de vista (fluido.js decide el sentido):
 *   · adelante  (de una lista a una ficha): la nueva entra por la derecha y
 *               la anterior se retira un poco hacia la izquierda.
 *   · atrás     el camino inverso.
 *   · lateral   (entre secciones del mismo nivel): fundido con un leve ascenso.
 * Solo se mueve el contenido: la barra lateral, la cabecera y las pestañas
 * tienen nombre propio y se quedan quietas.
 * -------------------------------------------------------------------------- */
.container { view-transition-name: contenido; }
.sidebar { view-transition-name: barra-lateral; }
.topbar { view-transition-name: cabecera; }
.tabbar { view-transition-name: pestanas; }
.main-bar { view-transition-name: franja; }

::view-transition-group(contenido) { animation: none; }
::view-transition-old(contenido) { animation: vt-sale 160ms ease-out both; }
::view-transition-new(contenido) { animation: vt-entra var(--muelle-rapido-dur) var(--muelle-rapido) both; }
/* La que se va, deprisa (180 ms): así la nueva entra sobre un fondo casi
   limpio y las dos no se mezclan a mitad de camino. */
html[data-nav="adelante"]::view-transition-old(contenido) { animation: vt-sale-izq 180ms ease-out both; }
html[data-nav="adelante"]::view-transition-new(contenido) { animation: vt-entra-der var(--muelle-rapido-dur) var(--muelle-rapido) both; }
html[data-nav="atras"]::view-transition-old(contenido) { animation: vt-sale-der 180ms ease-out both; }
html[data-nav="atras"]::view-transition-new(contenido) { animation: vt-entra-izq var(--muelle-rapido-dur) var(--muelle-rapido) both; }

/* El selector de pantallas de una sección («Ver obras / Cashflow») no se va
   con el contenido: se queda donde está y su píldora se desliza hasta la
   pantalla nueva (fluido.js). Si en la pantalla de antes no lo había, entra
   y sale como el resto. */
.tabs-seccion { view-transition-name: selector-seccion; }
::view-transition-group(selector-seccion) { animation-duration: var(--muelle-rapido-dur); animation-timing-function: var(--muelle-rapido); }
::view-transition-old(selector-seccion) { animation: none; opacity: 0; }
::view-transition-new(selector-seccion) { animation: none; }
::view-transition-old(selector-seccion):only-child { animation: vt-sale 160ms ease-out both; opacity: 1; }
::view-transition-new(selector-seccion):only-child { animation: vt-entra var(--muelle-rapido-dur) var(--muelle-rapido) both; }

@keyframes vt-sale     { to { opacity: 0; } }
@keyframes vt-entra    { from { opacity: 0; transform: translateY(8px); } }
@keyframes vt-sale-izq { to { opacity: 0; transform: translateX(-28px); } }
@keyframes vt-entra-der { from { opacity: 0; transform: translateX(44px); } }
@keyframes vt-sale-der { to { opacity: 0; transform: translateX(44px); } }
@keyframes vt-entra-izq { from { opacity: 0; transform: translateX(-28px); } }

/* ---- 8. Controles segmentados: la píldora se desliza ------------------------ *
 * fluido.js pone detrás de las opciones una píldora propia (`.seg-pildora`)
 * y la mueve con un muelle de una opción a otra, también cuando el control
 * se vuelve a pintar entero. La opción activa deja de pintar su fondo: lo
 * pone la píldora.
 * -------------------------------------------------------------------------- */
.con-pildora { position: relative; isolation: isolate; }
.seg-pildora {
  position: absolute; left: 0; top: 0; z-index: 0;
  border-radius: 9px; background: var(--surface);
  box-shadow: 0 1px 2px rgba(48, 41, 41, 0.08), 0 3px 8px rgba(48, 41, 41, 0.06);
  pointer-events: none;
}
.con-pildora > :not(.seg-pildora) { position: relative; z-index: 1; }
.con-pildora > :is(.active, .activo) { background: transparent !important; box-shadow: none !important; }
/* El imán del desplazamiento (scroll-snap, en el móvil) respeta el relleno del
   carril: si no, deja el carril corrido 3 px y parece que hay algo a la
   izquierda. */
.tabs-seccion { scroll-padding-inline: 3px; }
/* Si las opciones no caben y ruedan de lado, el borde se desvanece hacia el
   lado por el que hay más (fluido.js marca hacia dónde). */
.desborda-der { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
.desborda-izq { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent); }
.desborda-izq.desborda-der { -webkit-mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent); }

/* ---- 9. Filas que se deslizan para borrar ---------------------------------- *
 * Como en las listas de iOS: con el dedo, la fila se arrastra hacia la
 * izquierda y debajo aparece «Eliminar». fluido.js lleva el gesto; aquí va
 * la forma. Las filas dejan el desplazamiento vertical al navegador
 * (`pan-y`) y se quedan el horizontal: las tablas nunca ruedan de lado.
 * -------------------------------------------------------------------------- */
@media (pointer: coarse) {
  :is(.row-obra, .row-cli, .row-crm):not(.row-head) { touch-action: pan-y; }
}
/* Se mueve el contenido de la fila; la fila (y su separador) se queda. */
.deslizando > :not(.fila-accion) { transform: translateX(var(--desliz, 0px)); transition: none; }
.deslizando:active { box-shadow: none !important; transform: none !important; }
.fila-accion {
  position: absolute; top: 0; right: 0; bottom: 0; z-index: 2;
  width: 0; overflow: hidden;
  display: flex; align-items: center;
  padding: 0 0 0 22px; border: 0;
  background: var(--danger); color: #fff;
  font: inherit; font-size: 15px; font-weight: 600; letter-spacing: -0.01em;
  white-space: nowrap; cursor: pointer;
}

/* ---- 10. Foco -------------------------------------------------------------- */
/* El anillo de foco del teclado aparece acercándose, no de golpe. */
:focus-visible { transition: outline-offset var(--muelle-rapido-dur) var(--muelle-rapido); }

/* ---- 11. Accesibilidad ----------------------------------------------------- */
/* Menos movimiento: nada se desliza ni rebota; lo que ayuda a entender (la
   aparición de un diálogo) se queda en un fundido corto. `styles.css` ya
   anula duraciones; aquí se cambian además las formas del movimiento. */
@media (prefers-reduced-motion: reduce) {
  .modal-overlay > .modal,
  .modal-overlay.saliendo > .modal { animation-name: velo-entra !important; animation-duration: 150ms !important; }
  .modal-overlay.saliendo > .modal { animation-name: velo-sale !important; }
  :is(.btn, .acceso-card, .cert-card, .doc-card, .modo-card, .proy-card, .crm-card):active { transform: none !important; }
  ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
  .barra-titulo { transform: none !important; }
  html.titulo-en-barra .topbar .barra-titulo, .topbar .barra-titulo { transform: translateX(-50%) !important; }
}
/* Menos transparencia: los cristales se vuelven opacos. Las barras de
   arriba, del color de la página (opacas, pero sin hacer de franja blanca);
   la de pestañas, blanca. */
@media (prefers-reduced-transparency: reduce) {
  html.desplazado .main-bar, html.desplazado .topbar { background: var(--bg) !important; }
  .tabbar, .menu-flot, .estado-menu, .fase-menu, .combo-panel, .perfil-panel { background: var(--surface) !important; }
  .main-bar, .topbar, .tabbar, .menu-flot, .estado-menu, .fase-menu, .combo-panel, .perfil-panel, .modal-overlay {
    -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
  }
}
/* Más contraste: fondos sólidos y bordes definidos, en vez de desvanecidos. */
@media (prefers-contrast: more) {
  :root { --filete: rgba(48, 41, 41, 0.45); --pulsado: rgba(48, 41, 41, 0.12); }
  html.desplazado .main-bar, html.desplazado .topbar { background: var(--bg) !important; box-shadow: 0 1px 0 var(--filete); }
  .main-bar::after, .topbar::after, .app-shell::after { display: none; }
  .tabbar { background: var(--surface) !important; box-shadow: 0 0 0 1px var(--filete) !important; }
  .menu-flot, .estado-menu, .fase-menu, .combo-panel, .perfil-panel { background: var(--surface); border-color: var(--border-strong); }
  .seg-pildora { box-shadow: 0 0 0 1px var(--border-strong); }
}
