/* ═══════════════════════════════════════════════════════════════
   CONTROL URBANO V6 · styles.css · Sistema Unificado (Mayo 2026)
   Editorial · Terracotta sobre crema
   ───────────────────────────────────────────────────────────────
   ORGANIZACIÓN
     1) TOKENS               (color, espaciado, radio, sombra, tipografía)
     2) RESET / BASE
     3) UTILIDADES DE TIPO   (.t-label, .t-mono)
     4) LAYOUT               (wrapper, header, nav, pantallas, sidebar)
     5) COMPONENTES          (.btn, .badge-suave, .stat-box-v2, .input, .card, .chip)
     6) PUENTE LEGACY        (clases viejas → look nuevo, sin romper JSX)
     7) FEATURES ESPECÍFICAS (gps, agenda, filtros buscar, etc.)
     8) OVERLAYS / OFFLINE
     9) PRINT
   ═══════════════════════════════════════════════════════════════ */


/* ╔════════════════════════════════════════════════════════════╗
   ║ 1) TOKENS                                                  ║
   ╚════════════════════════════════════════════════════════════╝ */
:root {
  /* ── Color: tinta cálida + superficie crema ──────────────── */
  --ink:        #1F1B16;   /* tinta café-negra cálida */
  --ink-2:      #2A2520;
  --ink-3:      #5C5142;   /* texto secundario */
  --ink-4:      #806F5A;   /* texto terciario / placeholders */
  --line:       rgba(31,27,22,0.10);   /* borde sutil */
  --line-2:     rgba(31,27,22,0.18);   /* borde nítido */
  --paper:      #F5F1EB;   /* fondo crema papel */
  --surface:    #FFFBF5;   /* superficie casi blanca */
  --surface-2:  #ECE5D9;   /* superficie destacada (segmented bg) */
  --surface-3:  #DED5C8;

  /* ── Brand + semánticos (5 colores funcionales) ─────────── */
  /* QW4 (auditoría 2026-07): --brand y --amber oscurecidos en L (mismo H/S)
     para cumplir AA 4.5:1 sobre --surface (botón primario) y su -bg (badge).
     Antes #C96442/#B8873A medían 3.78/2.85 — ver INFORME_EJECUTIVO_PASS3.md. */
  --brand:      #AC5031;   /* terracotta · acción / pendiente */
  --brand-ink:  #8A3F26;   /* ladrillo · hover / danger */
  --brand-bg:   #FBE9E0;   /* terracotta tinte fondo */
  --brand-glow: rgba(172,80,49,0.18);

  --info:       #4A6B7C;   /* azul polvoriento · iniciado / info */
  --info-bg:    #E0E8EC;
  --green:      #6B7A3A;   /* olivo · completado / éxito */
  --green-bg:   #EFF1E1;
  --green-ink:  #565E2E;
  --amber:      #8D682D;   /* ocre · asignado / atención */
  --amber-bg:   #FAF1DD;
  --red:        #B43A2E;   /* ladrillo · error / suspensión */
  --red-bg:     #F8E4DF;

  /* ── Espaciado (base 4) ──────────────────────────────────── */
  --s-1: 4px;   --s-2: 8px;    --s-3: 12px;  --s-4: 16px;
  --s-5: 24px;  --s-6: 32px;   --s-7: 48px;  --s-8: 64px;  --s-9: 96px;

  /* ── Radio (4 tokens) ────────────────────────────────────── */
  --r-sm:   6px;
  --r:      10px;
  --r-lg:   16px;
  --r-full: 999px;

  /* ── Elevación (2 sombras) ───────────────────────────────── */
  --shadow-sm: 0 1px 2px rgba(31,27,22,0.05), 0 1px 1px rgba(31,27,22,0.03);
  --shadow-md: 0 12px 28px -12px rgba(31,27,22,0.18), 0 2px 4px rgba(31,27,22,0.04);

  /* ── Tipografía ──────────────────────────────────────────── */
  --font-serif: 'Source Serif 4', 'Source Serif Pro', Georgia, 'Times New Roman', serif;
  --font-sans:  'Inter', -apple-system, system-ui, 'Helvetica Neue', sans-serif;
  --font-mono:  'JetBrains Mono', 'SF Mono', Monaco, Consolas, monospace;

  /* ── Layout ──────────────────────────────────────────────── */
  --sidebar-w:   256px;
  --content-max: 740px;
  /* Valor de arranque: la barra superior no tiene alto fijo (crece cuando
     el título o el nombre del inspector envuelven), así que app.jsx mide
     la real y reescribe esta variable. No la trates como una constante:
     lo era, y por eso la cabecera de la visita se pegaba bajo la barra. */
  --header-h:    49px;
  /* Padding superior del área de contenido, por ancho. Lo necesita
     .nv-header para taparlo cuando queda fija. */
  --content-pad-top: 16px;
  /* Alto de la cabecera fija de la visita (medido: 153 px en móvil, 190 en
     escritorio). NO incluye la barra de la app: quien la necesite la suma
     con --header-h. Lo usan el scroll a sección y el tablero. */
  --nv-header-h: 160px;

  /* ── Piso táctil ─────────────────────────────────────────────
     App de campo: el inspector la usa de pie, en la calle, con una
     mano y a veces con guantes. Apple HIG pide 44pt, Material 48dp.
     Antes solo .btn-principal (48px) y .fab (54px) llegaban; el resto
     de los accionables medía entre 25px (.btn-limpiar) y 40px (.btn).
     Los botones "pequeños" siguen viéndose pequeños — lo que crece es
     el área de impacto, no la caja visual. */
  --tap:      44px;
  --tap-min:  36px;   /* controles densos en fila (chips, filtros) */

  /* ╭─────────────────────────────────────────────────────────╮ */
  /* │ ALIAS LEGACY                                             │ */
  /* │ Mantienen viva la API CSS vieja. No usar en código nuevo │ */
  /* ╰─────────────────────────────────────────────────────────╯ */
  --brand-accent: var(--brand);
  --brand-med:    var(--ink-2);
  --brand-light:  #3F362B;
  --texto:        var(--ink);
  --texto-2:      var(--ink-2);
  --texto-suave:  var(--ink-3);
  --texto-3:      var(--ink-4);
  --superficie:   var(--surface);
  --fondo:        var(--paper);
  --borde:        var(--line);
  --borde-med:    var(--line-2);
  --verde:        var(--green);
  --verde-bg:     var(--green-bg);
  --verde-dark:   var(--green-ink);
  --rojo:         var(--red);
  --rojo-bg:      var(--red-bg);
  --naranja:      var(--brand);
  --naranja-bg:   var(--brand-bg);
  --azul:         var(--info);
  --azul-bg:      var(--info-bg);
  --cafe:         var(--brand-ink);
  --cafe-bg:      var(--brand-bg);
  --amarillo:     var(--amber);
  --amarillo-bg:  var(--amber-bg);
  --slate-900:    var(--ink);
  --slate-800:    var(--ink-2);
  --slate-700:    #3F362B;
  --slate-600:    var(--ink-3);
  --slate-500:    var(--ink-4);
  /* QW4: oscurecido (mismo H/S) para AA 4.5:1 en .nav-btn/.bottom-tab inactivo — antes #A99A85 medía 2.44:1 sobre --surface */
  --slate-400:    #81715B;
  --slate-300:    #C9C0B5;
  --slate-200:    var(--surface-3);
  --slate-100:    var(--surface-2);
  --slate-50:     var(--paper);
  --gris-bg:      var(--surface-2);
  --sombra-xs:    var(--shadow-sm);
  --sombra-sm:    var(--shadow-sm);
  --sombra:       var(--shadow-sm);
  --sombra-md:    var(--shadow-md);
  --sombra-lg:    var(--shadow-md);
  --r-xs:         4px;
  --r-md:         var(--r);
  --r-xl:         20px;
  --radio:        var(--r);
  --radio-sm:     var(--r-sm);
}


/* ╔════════════════════════════════════════════════════════════╗
   ║ 2) RESET / BASE                                            ║
   ╚════════════════════════════════════════════════════════════╝ */
*, *::before, *::after {
  box-sizing: border-box; margin: 0; padding: 0;
  -webkit-tap-highlight-color: transparent;
}
html { height: 100%; }
body {
  font-family: var(--font-sans);
  background: var(--paper);
  color: var(--ink);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  font-size: 15px;
  line-height: 1.55;
  text-rendering: optimizeLegibility;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.oculto { display: none !important; }


/* ╔════════════════════════════════════════════════════════════╗
   ║ 3) UTILIDADES DE TIPO                                      ║
   ║    Reemplazan los ~140 inline styles de los JSX            ║
   ╚════════════════════════════════════════════════════════════╝ */
.t-label {
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-3);
}
.t-mono  { font-family: var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: 0.2px; }


/* ╔════════════════════════════════════════════════════════════╗
   ║ 4) LAYOUT                                                  ║
   ╚════════════════════════════════════════════════════════════╝ */

#app-wrapper {
  max-width: 430px;
  margin: 0 auto;
  min-height: 100vh;
  position: relative;
  background: var(--paper);
}

/* ── Iconos (Heroicons outline · stroke-width 1.6 unificado) ── */
.ico {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.ico svg { width: 20px; height: 20px; stroke-width: 1.6; }
.ico-sm svg { width: 16px; height: 16px; }

/* ── Header ─────────────────────────────────────────────── */
.header {
  background: rgba(255,251,245,0.94);
  backdrop-filter: saturate(180%) blur(24px);
  -webkit-backdrop-filter: saturate(180%) blur(24px);
  color: var(--ink);
  padding: 12px 20px 11px;
  position: sticky; top: 0; z-index: 200;
  border-bottom: 1px solid var(--line);
}
.header-top {
  display: flex; justify-content: space-between;
  align-items: center; gap: 12px;
}
.header h1 {
  font-family: var(--font-serif);
  font-size: 17px; font-weight: 600;
  letter-spacing: -0.2px; color: var(--ink);
}
.header p {
  font-size: 11px; color: var(--ink-3);
  margin-top: 1px; font-weight: 400;
}
.btn-logout {
  background: transparent;
  border: 1px solid var(--line-2);
  color: var(--ink-2);
  padding: 6px 14px; border-radius: var(--r-full);
  font-size: 12px; font-weight: 500;
  cursor: pointer; font-family: inherit;
  transition: background .15s, color .15s, border-color .15s;
  white-space: nowrap;
}
@media (hover: hover) and (pointer: fine) {
  .btn-logout:hover {
    background: var(--brand-bg);
    color: var(--brand);
    border-color: var(--brand);
  }
}
.btn-logout:active { transform: scale(0.96); }

/* ── Login (fondo oscuro cálido) ───────────────────────────── */
#pantalla-login {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  min-height: 100vh; padding: 48px 32px;
  background: linear-gradient(160deg, #0F0B08 0%, var(--ink) 50%, var(--ink-2) 100%);
  text-align: center;
}
.login-logo {
  width: 92px; height: 92px;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 24px;
  filter: drop-shadow(0 12px 24px rgba(0,0,0,0.45));
}
.login-logo img {
  width: 100%; height: 100%;
  object-fit: contain;
}
.login-titulo {
  font-family: var(--font-serif);
  color: var(--paper);
  font-size: 44px; font-weight: 600;
  text-align: center; margin-bottom: 6px;
  letter-spacing: -1px; line-height: 1.05;
}
.login-sub {
  color: rgba(245,241,235,0.55);
  font-size: 15px; text-align: center;
  margin-bottom: 36px; font-weight: 400;
}
.login-form { width: 100%; max-width: 360px; }
.login-input {
  width: 100%; padding: 15px 16px;
  border-radius: var(--r);
  font-size: 16px; font-family: inherit;
  margin-bottom: 10px; outline: none;
  background: rgba(245,241,235,0.06);
  color: var(--paper);
  border: 1px solid rgba(245,241,235,0.10);
  transition: border-color .15s, background .15s;
}
.login-input::placeholder { color: rgba(245,241,235,0.35); }
.login-input:focus {
  border-color: var(--brand);
  background: rgba(245,241,235,0.10);
}
.login-input option { background: var(--ink); color: var(--paper); }
.btn-login {
  width: 100%; padding: 15px;
  border-radius: var(--r); border: none;
  background: var(--brand); color: var(--surface);
  font-size: 16px; font-weight: 600;
  cursor: pointer; font-family: inherit;
  box-shadow: var(--shadow-md);
  transition: background .15s, transform .1s;
  letter-spacing: -0.1px;
}
@media (hover: hover) and (pointer: fine) {
  .btn-login:hover { background: var(--brand-ink); }
}
.btn-login:active { transform: scale(0.97); }
.login-nota {
  color: rgba(245,241,235,0.32);
  font-size: 11px; text-align: left;
  margin-top: 24px;
}

/* ── Pantallas ─────────────────────────────────────────────── */
.pantalla {
  display: none;
  padding: 16px 16px 96px;
  min-height: calc(100vh - var(--header-h));
}
.pantalla.activa { display: block; }

.page-title {
  font-family: var(--font-serif);
  font-size: 32px; font-weight: 600;
  color: var(--ink); letter-spacing: -0.8px;
  margin-bottom: 20px; margin-top: 4px;
  line-height: 1.1;
}
/* ── Pad bottom (espacio sobre nav inferior fija) ──────────── */
.pad-bottom { padding-bottom: 100px; }


/* ╔════════════════════════════════════════════════════════════╗
   ║ 5) COMPONENTES                                             ║
   ╚════════════════════════════════════════════════════════════╝ */

/* ── 5.1 Botones ────────────────────────────────────────────
       4 variantes funcionales: primary · secondary · ghost · danger
       3 tamaños: sm · default · lg
   ──────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center; justify-content: center;
  gap: 8px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.1px;
  padding: 11px 18px;
  min-height: var(--tap);
  border-radius: var(--r);
  border: 1px solid transparent;
  cursor: pointer;
  line-height: 1.2;
  transition: background .12s, border-color .12s, color .12s, transform .08s;
  white-space: nowrap;
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

.btn-primary    { background: var(--brand);    color: var(--surface); box-shadow: 0 4px 12px var(--brand-glow); }
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover { background: var(--brand-ink); }
}

/* `.btn-sm` como modificador de `.btn` se eliminó: el markup nunca usó el
   combo `btn btn-sm`, y su declaración quedaba ANTES del `.btn-sm` autónomo
   de la sección 6, que la pisaba entera (fondo, borde) anulando a
   `.btn-primary`. La definición viva es la de la sección 6. */

/* ── Cabeceras colapsables (acordeones/filtros) ────────────────
   Reset para usarlas como <button> nativo: accesible por teclado y con
   estado anunciable, sin perder los estilos inline de cada pantalla. */
.btn-cabecera {
  width: 100%;
  background: none;
  border: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  padding: 0;
}

/* ── Foco visible global por teclado ───────────────────────────
   Los inputs ya tienen su propio anillo (:focus); los botones/chips no
   tenían NINGÚN indicador de foco de teclado. */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* ── 5.5 Card genérico ───────────────────────────────────── */
.card {
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: 16px;
  margin-bottom: 12px;
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--line);
}
.card-titulo {
  font-family: var(--font-serif);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: 12px;
  letter-spacing: -0.1px;
}

/* ── 5.6 Input ────────────────────────────────────────────── */
.input-grupo { margin-bottom: 14px; }
.input-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-3);
  margin-bottom: 6px;
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.input-campo {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  font-size: 15px;
  font-family: inherit;
  color: var(--ink);
  background: var(--surface);
  outline: none;
  transition: border-color .12s, box-shadow .12s;
  -webkit-appearance: none;
}
.input-campo:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-glow);
}
.input-campo.mono {
  font-family: var(--font-mono);
  letter-spacing: 0.2px;
}
.input-campo::placeholder { color: var(--ink-3); }   /* WCAG AA: ~6.4:1 sobre crema */
select.input-campo {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 24 24' stroke='%23806F5A' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px;
}
textarea.input-campo { resize: vertical; min-height: 80px; line-height: 1.5; }

/* ── 5.7 Radio buttons (chips de opción) ────────────────── */
.radio-grupo { display: flex; gap: 6px; flex-wrap: wrap; }
/* `flex: 1` con `min-width: 60px` repartía el ancho en partes iguales y
   RECORTABA la etiqueta: en 375px el grupo Relación mostraba "Propietari",
   "Arrendata", "Construct". Ahora la base es el contenido (`flex: 1 1 auto`
   sobre `width: auto`), así que una opción larga empuja al grupo a envolver
   en vez de truncarse. Un radio que no se puede leer no se puede elegir. */
.radio-opcion {
  flex: 1 1 auto; min-width: 68px;
  /* 36-37 px de alto quedaban bajo el mínimo táctil; el inspector marca estos
     chips en campo, con guantes y una sola mano. */
  min-height: var(--tap);
  display: flex; align-items: center; justify-content: center;
  padding: 10px 12px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  text-align: center;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
  background: var(--surface);
  color: var(--ink-3);
  min-height: var(--tap-min);
  -webkit-user-select: none;
  /* _Radio ahora renderiza <button role="radio">: los botones no heredan
     tipografía del documento y traen estilo nativo. */
  font-family: inherit;
  appearance: none; -webkit-appearance: none;
}
@media (hover: hover) and (pointer: fine) {
  .radio-opcion:hover:not(.sel) {
    background: var(--surface-2);
  }
}
.radio-opcion.sel {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--surface);
  font-weight: 600;
}

/* ── 5.8 Chips (filtros / multi-select) ─────────────────── */
.chip,
.chip-inf,
.chip-vis {
  padding: 7px 13px;
  min-height: var(--tap-min);
  border-radius: var(--r-sm);
  border: 1px solid var(--line-2);
  background: var(--surface);
  color: var(--ink-3);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
  display: inline-flex; align-items: center; gap: 5px;
  -webkit-user-select: none;
  /* Son <button> desde la auditoría de sep-08: los botones no heredan
     tipografía ni pierden el aspecto nativo por sí solos. */
  font-family: inherit;
  appearance: none;
  -webkit-appearance: none;
}
@media (hover: hover) and (pointer: fine) {
  .chip:hover, .chip-inf:hover, .chip-vis:hover {
    background: var(--surface-2);
    color: var(--ink-2);
  }
}
.chip.activo,
.chip-inf.activo,
.chip-vis.activo {
  background: var(--brand);
  color: var(--surface);
  border-color: var(--brand);
  font-weight: 600;
}
.chips, .chips-inf {
  display: flex; flex-wrap: wrap;
  /* 8px, no 6: entre dos blancos táctiles contiguos, 6px es donde ocurre
     el fallo de toque con el pulgar en movimiento. */
  gap: 8px;
  margin-bottom: 14px;
}
.chips-inf { margin-bottom: 0; }


/* ╔════════════════════════════════════════════════════════════╗
   ║ 6) PUENTE LEGACY                                           ║
   ║    Clases viejas → look nuevo. Permite migrar JSX a su     ║
   ║    propio ritmo sin romper nada.                           ║
   ╚════════════════════════════════════════════════════════════╝ */

/* ── .btn-principal y variantes ──────────────────────────── */
.btn-principal {
  background: var(--brand); color: var(--surface);
  border: 1px solid transparent;
  padding: 14px 18px;
  /* El padding por sí solo ya daba 48px, pero varias pantallas lo pisan con
     `style={{ padding: '8px 12px' }}` y ahí caía a ~32px. El piso lo sostiene. */
  min-height: var(--tap);
  border-radius: var(--r);
  font-size: 15px; font-weight: 600;
  cursor: pointer; font-family: inherit;
  width: 100%;
  display: flex; align-items: center; justify-content: center;
  gap: 8px; margin-bottom: 10px;
  transition: background .12s, border-color .12s, color .12s, transform .08s;
  letter-spacing: -0.1px;
  box-shadow: 0 4px 12px var(--brand-glow);
}
@media (hover: hover) and (pointer: fine) {
  .btn-principal:hover { background: var(--brand-ink); }
}
.btn-principal:active { transform: scale(0.98); }
.btn-principal:disabled { opacity: .5; cursor: not-allowed; transform: none; }

/* Secundarios → outline terracotta (igual que .btn-secondary) */
.btn-principal.secundario,
.btn-principal.azul,
.btn-principal.cafe,
.btn-principal.verde {
  background: var(--surface);
  color: var(--brand);
  border-color: var(--brand);
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
  .btn-principal.secundario:hover,
  .btn-principal.azul:hover,
  .btn-principal.cafe:hover,
  .btn-principal.verde:hover {
    background: var(--brand-bg);
    color: var(--brand-ink);
    border-color: var(--brand-ink);
  }
}

/* Destructivo — rojo real, no terracotta oscuro.
   Antes usaba var(--brand-ink) (#8A3F26), que es EXACTAMENTE el color de
   hover del botón primario: "Eliminar" se veía igual que "Guardar" con el
   dedo encima. El rojo semántico (--red) ya existía y solo lo usaba
   .btn-sm.rojo, así que había dos lenguajes distintos para destruir. */
.btn-principal.rojo {
  background: var(--red);
  color: var(--surface);
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
.btn-principal.rojo:hover { background: #932E24; }
}

/* Neutro */
.btn-principal.gris {
  background: transparent;
  color: var(--ink-2);
  border-color: var(--line-2);
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
.btn-principal.gris:hover {
  background: var(--surface-2);
  color: var(--ink);
}
}

/* ── .btn-sm (pills pequeños) ────────────────────────────── */
.btn-sm {
  padding: 7px 13px;
  min-height: var(--tap-min);
  font-size: 12.5px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-2);
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer; font-family: inherit;
  font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  transition: background .12s, border-color .12s, color .12s, transform .08s;
  line-height: 1.2;
}
.btn-sm:active { transform: scale(0.96); }
@media (hover: hover) and (pointer: fine) {
.btn-sm:hover { background: var(--surface-2); }
}

/* Variantes legacy de .btn-sm — ahora con roles funcionales */
.btn-sm.brand,
.btn-sm.azul,
.btn-sm.cafe {
  background: var(--brand);
  color: var(--surface);
  border-color: var(--brand);
}
@media (hover: hover) and (pointer: fine) {
.btn-sm.brand:hover,
.btn-sm.azul:hover,
.btn-sm.cafe:hover { background: var(--brand-ink); border-color: var(--brand-ink); }
}

.btn-sm.verde {
  background: var(--green-bg);
  color: var(--green-ink);
  border-color: rgba(107,122,58,.25);
}
.btn-sm.gris {
  background: transparent;
  color: var(--ink-2);
  border-color: var(--line-2);
}
@media (hover: hover) and (pointer: fine) {
.btn-sm.gris:hover { background: var(--surface-2); }
}
.btn-sm.rojo {
  background: var(--red-bg);
  color: var(--red);
  border-color: rgba(180,58,46,.22);
}

/* ── Acciones de campo, cancelar y solo-icono ─────────────────
   Estas cuatro clases reemplazan 16 botones que se construían enteros
   con `style={{}}` en nueva-visita.jsx. El problema no era estético:
   medían entre 16px (Volver al inicio) y 32px de alto en una app que se
   usa de pie, en la calle, con una mano.

   `min-height` da el blanco táctil; el padding y el tamaño de letra
   siguen siendo pequeños, así que la caja visual no engorda. */

/* Acción secundaria de campo: GPS, mejorar con IA, dictar, consultar POT.
   Antes `_BtnAccion` con padding 6px/12px y font 12 → ~26px de alto. */
.btn-accion {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: var(--tap);
  padding: 9px 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--brand);
  background: var(--brand-bg);
  color: var(--brand-ink);
  font-family: inherit; font-size: 13px; font-weight: 600;
  line-height: 1.2; cursor: pointer;
  /* En los entregables esta clase también viste <a>: sin esto, «Abrir»
     hereda el subrayado del navegador y «Generar» no (misma caja, dos
     metáforas). */
  text-decoration: none;
  transition: background .12s, border-color .12s, color .12s, transform .08s;
}
.btn-accion:active { transform: scale(0.97); }
.btn-accion:disabled { opacity: .55; cursor: not-allowed; transform: none; }
@media (hover: hover) and (pointer: fine) {
  .btn-accion:not(:disabled):hover { background: #F6DACC; border-color: var(--brand-ink); }
}

/* Cancelar / retroceder. Un solo lenguaje: antes había SIETE apariencias
   distintas para la misma intención (texto plano, gris con borde, gris sin
   borde, outline ámbar, ancho completo…). */
.btn-neutro {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: var(--tap);
  padding: 11px 16px;
  border-radius: var(--r);
  border: 1px solid var(--line-2);
  background: transparent;
  color: var(--ink-2);
  font-family: inherit; font-size: 14px; font-weight: 600;
  line-height: 1.2; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s, transform .08s;
}
.btn-neutro:active { transform: scale(0.97); }
.btn-neutro:disabled { opacity: .5; cursor: not-allowed; transform: none; }
@media (hover: hover) and (pointer: fine) {
  .btn-neutro:not(:disabled):hover { background: var(--surface-2); color: var(--ink); }
}

/* Terciario: se lee como texto, pulsa como botón. Para escapes discretos
   ("Volver al inicio", "Cancelar" del GPS) que no deben competir con la
   acción principal pero tampoco medir 18px. */
.btn-texto {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: var(--tap);
  padding: 10px 12px;
  border: none; background: none;
  border-radius: var(--r-sm);
  color: var(--ink-3);
  font-family: inherit; font-size: 13px; font-weight: 600;
  line-height: 1.2; cursor: pointer;
  transition: background .12s, color .12s;
}
@media (hover: hover) and (pointer: fine) {
  .btn-texto:hover { background: var(--surface-2); color: var(--ink-2); }
}

/* Solo icono. Cuadrado de var(--tap) — antes el ✕ de eliminar foto medía
   24×28px y estaba pegado al input de descripción de la misma fila. */
.btn-icono {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap); height: var(--tap); flex-shrink: 0;
  padding: 0; border: none; background: none;
  border-radius: var(--r-sm);
  color: var(--ink-3); cursor: pointer;
  transition: background .12s, color .12s, transform .08s;
}
.btn-icono:active { transform: scale(0.92); }
.btn-icono.peligro { color: var(--red); }
@media (hover: hover) and (pointer: fine) {
  .btn-icono:hover { background: var(--surface-2); color: var(--ink); }
  .btn-icono.peligro:hover { background: var(--red-bg); color: var(--red); }
}

/* Checkbox con blanco táctil. El <input> nativo mide 16px y varios de los
   del formulario gobiernan si otro campo se habilita — el gesto de
   desbloqueo más importante de su sección medía 16px. La caja sigue
   viéndose de 18px; lo que crece es el área pulsable del <label>. */
.check-tap {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: var(--tap-min);
  padding: 4px 2px;
  cursor: pointer;
  font-size: 13px; color: var(--ink-3);
  -webkit-user-select: none;
}
.check-tap input[type="checkbox"] {
  width: 18px; height: 18px; flex-shrink: 0;
  accent-color: var(--brand);
  cursor: pointer;
}

/* ── FAB ─────────────────────────────────────────────────── */
.fab,
.fab-nueva {
  position: fixed;
  bottom: 76px; right: 16px;
  width: 54px; height: 54px;
  background: var(--brand);
  border-radius: var(--r-full);
  border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-md);
  z-index: 199;
  color: var(--surface);
  transition: transform .15s, background .15s;
}
@media (hover: hover) and (pointer: fine) {
.fab:hover, .fab-nueva:hover { background: var(--brand-ink); }
}
.fab:active, .fab-nueva:active { transform: scale(0.93); }

/* ── Filtros estado (legacy) → btn estilo unificado ─────── */
.filtros-estado {
  display: flex; gap: 6px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.btn-filtro {
  padding: 7px 13px;
  min-height: var(--tap-min);
  border-radius: var(--r-sm);
  border: 1px solid var(--line-2);
  background: var(--surface);
  color: var(--ink-3);
  font-size: 12px; font-weight: 600;
  cursor: pointer; font-family: inherit;
  transition: background .15s, color .15s, border-color .15s;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  letter-spacing: 0.05em;
}
@media (hover: hover) and (pointer: fine) {
.btn-filtro:hover { background: var(--surface-2); }
}
.btn-filtro:active { transform: scale(0.96); }
.btn-filtro.activo-pendiente,
.btn-filtro.activo-iniciado,
.btn-filtro.activo-comuna {
  background: var(--brand); color: var(--surface);
  border-color: var(--brand);
}
.btn-filtro.activo-completado {
  background: var(--green); color: var(--surface);
  border-color: var(--green);
}
.btn-limpiar {
  padding: 5px 10px;
  min-height: var(--tap-min);
  border-radius: var(--r-sm);
  border: 1px solid var(--line-2);
  background: transparent;
  color: var(--ink-3);
  font-size: 11px; font-weight: 600;
  cursor: pointer; font-family: inherit;
  transition: background .15s, color .15s;
  display: none; align-items: center; gap: 4px;
}
@media (hover: hover) and (pointer: fine) {
.btn-limpiar:hover { background: var(--red-bg); color: var(--red); border-color: var(--red); }
}
.btn-limpiar.visible { display: inline-flex; }
.filtros-comunas { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px; }
.filtros-comunas .btn-filtro { padding: 5px 10px; font-size: 11px; }

/* ── Agenda tabs (legacy) → look segmented unificado ────── */
.agenda-tabs {
  display: flex; gap: 4px;
  margin-bottom: 14px;
  background: var(--surface-2);
  border-radius: var(--r);
  padding: 3px;
}
.agenda-tab {
  flex: 1; padding: 8px 6px;
  font-size: 13px; font-weight: 600;
  border: none; border-radius: 7px;
  background: transparent;
  color: var(--ink-3);
  cursor: pointer; font-family: inherit;
  transition: background .15s, color .15s;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.agenda-tab.activo {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-sm);
}
@media (hover: hover) and (pointer: fine) {
.agenda-tab:hover:not(.activo) { color: var(--ink-2); }
}

/* badge-suave legacy → idéntico al badge unificado */
.badge-suave {
  font-size: 11px; font-weight: 600;
  padding: 3px 10px; border-radius: var(--r-full);
  white-space: nowrap; flex-shrink: 0;
  letter-spacing: 0.02em;
  border: 1px solid transparent;
  display: inline-flex; align-items: center; gap: 5px;
  text-transform: none;
}
.badge-suave.badge-amarillo { background: var(--amber-bg); color: var(--amber);    border-color: rgba(184,135,58,.25); }
.badge-suave.badge-azul     { background: var(--info-bg);  color: var(--info);     border-color: rgba(74,107,124,.22); }
.badge-suave.badge-rojo     { background: var(--red-bg);   color: var(--red);      border-color: rgba(180,58,46,.22); }
.badge-suave.badge-verde    { background: var(--green-bg); color: var(--green-ink);border-color: rgba(107,122,58,.25); }

/* stats-grid-2x2 legacy → mismo grid */
.stats-grid-2x2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px; margin-bottom: 16px;
}
.stats-grid-2x2.compact { gap: 8px; margin-bottom: 14px; }
.stat-box-v2 {
  background: var(--surface);
  border-radius: var(--r);
  padding: 16px 14px;
  box-shadow: none;
  border: 1px solid var(--line);
  position: relative;
  overflow: hidden;
}
.stat-box-v2::before {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--ink-4);
  border-radius: 0 2px 2px 0;
}
.stat-box-v2.acento::before { background: var(--brand); }
.stat-box-v2.verde::before  { background: var(--green); }
.stat-num-v2 {
  font-family: var(--font-serif);
  font-size: 36px; font-weight: 700;
  color: var(--ink); letter-spacing: -1.5px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.stat-box-v2.acento .stat-num-v2 { color: var(--brand); }
.stat-box-v2.verde  .stat-num-v2 { color: var(--green); }
.stat-label-v2 {
  font-size: 12px; font-weight: 600; color: var(--ink-2);
  margin-top: 6px; letter-spacing: -0.1px;
}
.stat-sub-v2 {
  font-size: 10px; color: var(--ink-3);
  margin-top: 2px; font-weight: 400;
}
.stats-grid-2x2.compact { gap: 8px; margin-bottom: 14px; }
.stat-box-v2.compact { padding: 12px; }
.stat-box-v2.compact .stat-num-v2 { font-size: 28px; letter-spacing: -1px; }
.stat-box-v2.compact .stat-label-v2 { font-size: 11px; margin-top: 4px; }


/* ╔════════════════════════════════════════════════════════════╗
   ║ 7) FEATURES ESPECÍFICAS                                    ║
   ╚════════════════════════════════════════════════════════════╝ */

/* ── GPS ─────────────────────────────────────────────────── */
/* La caja envuelve: con el botón ya a tamaño táctil, en 375px no caben la
   lectura y el botón en una fila y el texto del botón partía en dos líneas.
   Al envolver, el botón toma el ancho completo y se lee de un vistazo. */
.gps-box {
  background: var(--brand-bg);
  border: 1px solid rgba(201,100,66,.18);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 13px;
}
.gps-box > div:first-child { min-width: 150px; }
.gps-box > div:last-child  { flex: 1 1 auto; }
.gps-box .btn-accion { width: 100%; }
@media (min-width: 480px) {
  .gps-box > div:last-child { flex: 0 0 auto; }
  .gps-box .btn-accion { width: auto; }
}
.gps-coords {
  font-family: var(--font-mono);
  font-size: 12px; color: var(--brand-ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
}
.gps-dir { font-size: 11px; color: var(--ink-3); margin-top: 1px; }

/* Mapas. En móvil manda la pantalla; en escritorio sobra alto y el satelital
   a zoom 18 no se lee en 220px. */
.mapa-gps {
  width: 100%; height: 220px;
  border-radius: 10px;
  border: 1px solid var(--borde);
  overflow: hidden;
}
/* Sin mapa (cargando o sin señal): caja baja con el aviso dentro, en vez de
   220-500 px de gris vacío y el texto suelto encima. */
.mapa-gps.mapa-gps-sin {
  height: auto; min-height: 72px;
  display: flex; align-items: center; justify-content: center;
  padding: 16px; text-align: center;
  background: var(--surface-2); color: var(--ink-3);
  font-size: 12px;
}
.mapa-norma { width: 100%; height: 380px; }
@media (min-width: 768px) {
  .mapa-gps   { height: 420px; }
  .mapa-norma { height: 520px; }
}
@media (min-width: 1200px) {
  .mapa-gps   { height: 500px; }
  .mapa-norma { height: 600px; }
}

/* Usado por el indicador "Grabando..." del dictado por voz (nueva-visita) */
@keyframes pulsar { 0%,100%{opacity:1} 50%{opacity:.3} }

/* ── Formulario: secciones ──────────────────────────────── */
.form-seccion {
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: 16px;
  margin-bottom: 10px;
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--line);
}
/* Indicador binario a la derecha del título: check verde = completa,
   punto ámbar = con algo pendiente (solo tras un intento de generar
   fallido). Sin número y sin sección sin obligatorios marcada. El
   indicador es informativo: no es botón ni foco tabulable. */
.form-seccion-cabecera {
  display: flex; align-items: center; gap: 8px;
}
.sec-ind {
  flex-shrink: 0;
  /* Pisa el padding de la tarjeta en vez de robarle ancho al título: el
     título más largo («Características de la edificación», 284 px
     naturales) solo tiene ~9 px de holgura en un 390 y con el indicador
     en el flujo envolvería a dos líneas. Con este margen sigue dentro de
     la tarjeta (padding 16) y el título conserva su línea. */
  margin-right: -11px;
}
/* --green-ink y no --green: el ✓ es texto y la WCAG 1.4.3 pide 4.5:1
   (el --green se queda corto); el ink de la misma familia pasa de sobra. */
.sec-ind-ok { color: var(--green-ink); font-size: 16px; font-weight: 700; }
.sec-ind-pend {
  width: 10px; height: 10px; border-radius: 50%; /* mismo tamaño que .ent-dot */
  background: var(--amber); /* mismo lenguaje que .ent-dot.ed-pend */
}
.form-seccion-titulo,
.titulo-azul,
.titulo-cafe,
.titulo-gris,
.titulo-verde {
  font-family: var(--font-serif);
  font-size: 17px; font-weight: 600;
  letter-spacing: -0.2px;
  margin-bottom: 14px;
  display: inline-flex;
  align-items: center; gap: 8px;
  color: var(--brand);
  padding: 0 0 0 10px;
  border-left: 3px solid var(--brand);
  line-height: 1.2;
}

/* ── Centro de control de la visita (nueva-visita) ────────
   Panel de estado superior + renglones de entregables + barra de
   guardado con estado permanente. Solo variables existentes. */

/* Cabecera fija de la visita.
   Quién scrollea cambia por ancho: en móvil el documento (bajo el
   .app-header sticky de --header-h), en escritorio #content-desktop
   (overflow-y:auto), donde el tope es 0. El margen negativo + padding
   del mismo valor hace que la cabecera cubra el padding superior del
   scroller; sin eso el formulario asoma por encima al subir. */
.nv-header {
  position: sticky; top: var(--header-h); z-index: 30;
  background: var(--fondo);
  margin-top: calc(-1 * var(--content-pad-top));
  padding-top: var(--content-pad-top);
  padding-bottom: 12px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--borde);
}
/* El banner de sin conexión empuja la barra de la app 40 px hacia abajo
   (regla `body.is-offline .header`): la cabecera de la visita tiene que
   bajar lo mismo o se pega debajo de ella. */
body.is-offline .nv-header { top: calc(var(--header-h) + 40px); }
.nv-header-top {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin-bottom: 10px;
}
/* En móvil la cabecera completa se comía 208 px de 844 (25%), y con el
   .app-header y la barra de guardar encima queda medio formulario. Fija
   va solo la identidad: el título es genérico («Continuar visita») y los
   pendientes ya están en la zona de entregables. */
@media (max-width: 767px) {
  .nv-header .page-title { display: none; }
  .nv-header .estado-resumen { display: none; }
  .nv-header .estado-dir {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
}

/* ── Escritorio ancho: formulario + tablero de entregables ──────
   El escritorio es la oficina: revisar, corregir y generar. En una sola
   tira vertical hay que recorrer las diez secciones para llegar a los
   entregables en cada vuelta. Desde 1440 px el tablero pasa a una
   columna fija a la derecha; por debajo todo queda exactamente igual.
   1440 y no 1200: descontando la barra lateral (272) y el padding (96),
   a 1200 el formulario caería a ~510 px, más estrecho que un móvil
   ancho, y el formulario manda sobre el tablero. */
@media (min-width: 1440px) {
  /* .pantalla.activa fija display:block con dos clases: hay que igualar
     esa especificidad o el grid no se aplica. */
  .pantalla.nv-pantalla {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    column-gap: 24px;
    align-items: start;
    /* Ensancha solo esta pantalla: el formulario conserva el ancho de
       hoy (--content-max) y el tablero se suma al lado. */
    max-width: calc(var(--content-max) + 344px);
  }
  .nv-pantalla > * { grid-column: 1; }
  .nv-header { grid-column: 1 / -1; }
  .nv-entregables {
    grid-column: 2;
    /* Hasta una línea muy posterior a propósito: la celda tiene que
       abarcar todo el alto de la columna izquierda o el sticky se
       despegaría al acabar la primera sección. Las filas implícitas que
       crea van vacías y miden 0 (no hay row-gap). */
    grid-row: 2 / 200;
    align-self: start;
    position: sticky; top: var(--nv-header-h);
    /* Con 18 miniaturas y el escáner abierto la columna desborda la
       pantalla y el último botón queda inalcanzable. Abajo descuenta la
       barra fija de guardar (92 px medidos): con los 32 px de antes, el
       borde inferior del tablero quedaba 11 px debajo de ella. */
    max-height: calc(100vh - var(--header-h) - var(--nv-header-h) - 104px);
    overflow-y: auto;
    /* Al tabular hacia un control del tablero cuando la página está al
       fondo (ahí el tablero ya no está pegado), el scroll mínimo del foco
       lo dejaba debajo de la cabecera fija. */
    scroll-margin-top: calc(var(--nv-header-h) + 8px);
  }
  /* En 320 px la miniatura de escritorio (72 px) deja 3 columnas y 533 px
     de alto con 18 fotos; la celda de móvil deja 4 y ~330. La columna es
     estrecha, no una pantalla ancha. */
  .nv-entregables .fotos-grilla {
    max-width: none;
    grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
  }
}

/* Panel de estado superior */
.estado-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 10px 14px;
  display: flex; flex-direction: column; gap: 6px;
}
.estado-panel-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.estado-badge {
  font-size: 11px; font-weight: 700; letter-spacing: 0.4px;
  padding: 3px 10px; border-radius: var(--r-full);
}
/* Tonos por punto del ciclo de vida — mismos roles semánticos de la app:
   ámbar=pendiente, azul=asignado, terracota=iniciado, oliva=completado */
.ep-amarillo { background: var(--amber-bg); color: var(--amber); }
.ep-azul     { background: var(--info-bg);  color: var(--info); }
.ep-naranja  { background: var(--brand-bg); color: var(--brand-ink); }
.ep-verde    { background: var(--green-bg); color: var(--green-ink); }
.estado-id { font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--ink-3); }
.estado-visita-n {
  font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: var(--r-full);
  background: var(--brand-bg); color: var(--brand-ink); border: 1px solid var(--brand);
}
.estado-dir { font-size: 13px; font-weight: 600; color: var(--ink); }
.estado-resumen { display: flex; flex-wrap: wrap; gap: 6px 12px; }
.resumen-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-3); }
.rc-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--surface-3); flex-shrink: 0; }
.resumen-chip.rc-ok   .rc-dot { background: var(--green); }
.resumen-chip.rc-pend .rc-dot { background: var(--amber); }
.resumen-chip.rc-cola .rc-dot { background: var(--amber); box-shadow: 0 0 0 2px var(--amber-bg); }
.estado-aviso {
  font-size: 12px; color: var(--amber); background: var(--amber-bg);
  border: 1px dashed var(--amber); border-radius: var(--r-sm);
  padding: 8px 10px;
}

/* Zona de entregables: un renglón por pieza */
.ent-fila {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 2px; min-height: var(--tap);
  border-bottom: 1px solid var(--line);
}
.ent-fila-wrap:last-child .ent-fila,
.ent-fila-wrap:last-child .ent-slot { border-bottom: none; }
.ent-icono { color: var(--ink-4); display: inline-flex; flex-shrink: 0; }
.ent-cuerpo { flex: 1 1 150px; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ent-nombre { font-size: 13px; font-weight: 600; color: var(--ink); }
.ent-meta { font-size: 11px; color: var(--ink-4); }
/* Estado por color, no por texto: un punto pequeño con el estado como
   title + sr-only (el color solo no alcanza para lector de pantalla).
   Verde=listo, ámbar=pendiente o en cola, gris=todavía no aplica, rojo=
   error. Mientras procesa, el spinner reemplaza al punto. `ent-nota` es
   el único texto de estado visible permitido (conteo de fotos). */
.ent-estado-wrap { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; }
.ent-dot {
  width: 10px; height: 10px; border-radius: 50%;
  /* Tono apagado: --surface-3 daba 1.41:1 contra el fondo (invisible, WCAG
     1.4.11 pide 3:1). --ink-4 sigue siendo gris —«todavía no aplica», no un
     error— y llega a 4.69:1. */
  background: var(--ink-4); flex-shrink: 0;
}
.ent-dot.ed-ok    { background: var(--green); }
.ent-dot.ed-pend  { background: var(--amber); }
.ent-dot.ed-error { background: var(--red); }
.ent-nota { font-size: 12px; color: var(--ink-3); white-space: nowrap; }
/* Grid de dos columnas en vez de flex: la segunda columna reserva SIEMPRE
   los 44 px de la acción secundaria (↻), exista o no en ese renglón. Con
   flex, las filas sin ↻ empujaban el botón principal 50 px a la derecha y
   la columna de acciones brincaba entre renglones. El <input> oculto del
   escáner (display:none) no genera celda, así que no cuenta. */
.ent-acciones {
  display: grid; grid-template-columns: auto 44px;
  align-items: center; column-gap: 6px; flex-shrink: 0;
}
/* Acción de renglón: «Generar»/«Abrir» — .btn-accion ya asegura el alto
   táctil (var(--tap)); el min-width común las alinea en columna entre
   renglones. */
.ent-btn { min-width: 92px; }
.ent-slot { padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.ent-slot-fotos,
.ent-slot-orden { margin-top: 2px; }
/* Fotos en grilla y no en lista: 18 renglones de texto medían ~790 px de alto
   y empujaban el resto del formulario; en celdas de 56 px son ~3 filas. Sin
   plegar: el alto ya no es el problema y esconderlas obligaba a un clic para
   confirmar lo único que importa (que subieron). */
.fotos-grilla {
  margin-top: 14px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
  gap: 6px;
}
/* Escritorio: sin límite, auto-fill llena el ancho disponible en una tira
   de columnas diminutas (17 columnas de 56 px a 1366, con 18 fotos dejando
   una segunda fila de una sola celda). Acotar la grilla y subir el mínimo
   deja celdas más grandes repartidas en varias filas. Móvil no cambia:
   en 390 px quedan 4 columnas limpias. */
@media (min-width: 768px) {
  .fotos-grilla {
    max-width: 560px;
    grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  }
}
.fg-celda {
  position: relative; display: block;
  aspect-ratio: 1;
  border-radius: 8px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line);
}
.fg-celda img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* El número va encima de la miniatura y sobrevive a que la imagen no cargue
   (en campo, sin señal): la cuenta se sigue leyendo aunque no haya foto. */
.fg-celda .fg-num {
  position: absolute; left: 0; right: 0; bottom: 0;
  font-size: 10px; line-height: 1.7; text-align: center;
  color: var(--ink-3); background: rgba(255, 255, 255, 0.82);
}
.fg-celda.fg-pendiente {
  border: 1px dashed var(--amarillo); background: var(--amarillo-bg);
  display: flex; align-items: center; justify-content: center;
}
.fg-celda.fg-pendiente .fg-num {
  position: static; background: none; font-size: 15px; color: var(--amarillo);
}
.ent-aviso {
  font-size: 13px; color: var(--ink-3); background: var(--surface-2);
  border: 1px dashed var(--line-2); border-radius: var(--r-sm);
  padding: 12px 14px;
}

/* Barra de guardado con estado permanente (sobre el botón, barra fija) */
.barra-estado-gs {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  font-size: 11px; color: var(--ink-4); text-align: center;
  margin-bottom: 6px; min-height: 16px;
}
.barra-estado-gs.bgs-pendiente  { color: var(--amber); font-weight: 600; }
.barra-estado-gs.bgs-cola       { color: var(--amber); font-weight: 600; }
.barra-estado-gs.bgs-error      { color: var(--red);   font-weight: 600; }
.barra-estado-gs.bgs-guardando  { color: var(--ink-3); }

/* ── Nav inferior + sidebar desktop ─────────────────────── */
.nav-inferior {
  position: fixed; bottom: 0;
  left: 50%; transform: translateX(-50%);
  width: 100%; max-width: 430px;
  background: rgba(255,251,245,0.94);
  backdrop-filter: saturate(180%) blur(24px);
  -webkit-backdrop-filter: saturate(180%) blur(24px);
  border-top: 1px solid var(--line-2);
  display: flex; z-index: 200;
  padding-bottom: env(safe-area-inset-bottom);
}
.nav-btn,
.bottom-tab {
  flex: 1; padding: 8px 4px 6px;
  border: none; background: none; cursor: pointer;
  font-family: inherit;
  display: flex; flex-direction: column;
  align-items: center; gap: 2px;
  color: var(--slate-400);
  font-size: 10px; font-weight: 500;
  transition: color .15s;
  letter-spacing: 0.05em;
}
.nav-btn:active, .bottom-tab:active { opacity: .6; }
.nav-btn.activo, .bottom-tab.activo { color: var(--brand); }
.nav-btn svg, .bottom-tab svg { width: 22px; height: 22px; stroke-width: 1.6; }

#sidebar-desktop { display: none; }

/* ── Agenda (cards / strips) ────────────────────────────── */
.visita-card {
  background: var(--surface);
  border-radius: var(--r);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
  padding: 14px;
  margin-bottom: 10px;
  position: relative;
}
.visita-card-header {
  display: flex; justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 6px;
}
.visita-radicado {
  font-size: 12px; font-weight: 600;
  color: var(--ink-3);
  font-family: var(--font-mono);
  letter-spacing: 0.2px;
}
.visita-direccion {
  font-size: 13px; color: var(--ink-2);
  margin-bottom: 4px; font-weight: 500;
}
.visita-meta {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; margin-top: 6px;
}
.visita-meta-item { font-size: 11px; color: var(--ink-3); }

.score-badge {
  display: inline-flex; align-items: center;
  padding: 3px 8px;
  border-radius: var(--r-full);
  font-size: 11px; font-weight: 700;
  flex-shrink: 0;
}
.score-critico { background: var(--red-bg);   color: var(--red); }
.score-alto    { background: var(--brand-bg); color: var(--brand); }
.score-medio   { background: var(--amber-bg); color: var(--amber); }
.score-bajo    { background: var(--green-bg); color: var(--green-ink); }

.agenda-empty {
  text-align: center; padding: 40px 20px;
  color: var(--ink-3);
}

/* ── Inspector chips (agenda) ────────────────────────────── */
.inspector-chips {
  display: flex; gap: 6px; flex-wrap: wrap;
  margin: 10px 0 4px;
}
.inspector-chip {
  padding: 5px 12px;
  border-radius: var(--r-full);
  font-size: 12px; font-weight: 600;
  border: 1px solid var(--line-2);
  background: var(--surface);
  color: var(--ink-3);
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}
.inspector-chip.sel {
  background: var(--brand);
  color: var(--surface);
  border-color: var(--brand);
}
@media (hover: hover) and (pointer: fine) {
.inspector-chip:hover:not(.sel) { background: var(--surface-2); }
}

.agenda-confirmar-bar {
  display: flex; gap: 8px;
  padding: 12px 0 4px;
  border-top: 1px solid var(--line);
  margin-top: 12px;
}

/* ── Ajuste manual de la jornada: comuna + nº de visitas ── */
.agenda-ajuste-fila {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.agenda-comuna-total {
  margin-left: 6px;
  font-weight: 500; opacity: .75;
  font-variant-numeric: tabular-nums;
}
.agenda-restablecer {
  background: none; border: none; padding: 4px 0;
  font-family: inherit; font-size: 12px; font-weight: 600;
  color: var(--brand-accent);
  cursor: pointer; flex-shrink: 0;
}
.agenda-stepper {
  display: inline-flex; align-items: center; flex-shrink: 0;
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--surface);
  overflow: hidden;
}
.agenda-stepper button {
  width: 40px; height: 36px;
  border: none; background: transparent;
  color: var(--ink); font-family: inherit; font-size: 18px; line-height: 1;
  cursor: pointer;
}
.agenda-stepper button:disabled { color: var(--ink-4); opacity: .5; cursor: default; }
@media (hover: hover) and (pointer: fine) {
.agenda-stepper button:hover:not(:disabled) { background: var(--surface-2); }
}
.agenda-stepper span {
  min-width: 28px; text-align: center;
  font-size: 15px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── Buscar (sección filtros) ────────────────────────────── */
.filtro-section-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 0 6px;
  cursor: pointer; user-select: none;
}
.filtro-section-titulo {
  font-size: 11px; font-weight: 700;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.filtro-section-chevron {
  font-size: 10px; color: var(--ink-4);
  transition: transform .2s;
}
.filtro-section-chevron.abierto { transform: rotate(180deg); }


/* ╔════════════════════════════════════════════════════════════╗
   ║ 8) OVERLAYS / NOTIF / OFFLINE                              ║
   ╚════════════════════════════════════════════════════════════╝ */

/* Modales: fade en desktop, slide-up sheet en móvil */
@keyframes fadeIn {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes slideUpSheet {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
@keyframes dlgOverlayIn { from { opacity: 0; } to { opacity: 1; } }


/* ── 8.1 Diálogo de aviso / confirmación (modal.jsx) ──────────
   Antes vivía 100% en estilos inline: sin :hover posible, sin
   :focus-visible, radio 8px (los tokens son 6/10/16), texto 13px
   (el cuerpo es 15px) y —lo importante— SIN TONO: los 42 avisos del
   formulario de visita renderizaban idénticos, así que "Error al
   guardar" y "Acta generada" se distinguían solo leyendo.

   Regla de color: el ICONO dice qué pasó (verde/rojo/ocre/azul);
   el BOTÓN dice qué vas a hacer (terracotta = seguir, rojo = destruir).
   Nunca se mezclan: un aviso de error no pinta el botón de rojo, porque
   el botón solo cierra. */
.dlg-overlay {
  position: fixed; inset: 0; z-index: 9999;
  background: rgba(31,27,22,0.45);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  display: flex; justify-content: center; align-items: flex-end;
  animation: dlgOverlayIn .18s ease-out;
}
.dlg {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-md);
  width: 100%;
  max-height: 88vh;
  overflow-y: auto;
  padding: 18px 20px calc(18px + env(safe-area-inset-bottom));
  font-family: var(--font-sans);
  animation: slideUpSheet .25s cubic-bezier(0.32,0.72,0,1);
}
.dlg-grip {
  width: 36px; height: 4px; border-radius: 2px;
  background: var(--line-2);
  margin: -4px auto 16px;
}
.dlg-head { display: flex; gap: 13px; align-items: flex-start; }
.dlg-icono {
  flex: 0 0 38px; width: 38px; height: 38px;
  border-radius: var(--r-full);
  display: flex; align-items: center; justify-content: center;
}
.dlg-icono svg { width: 20px; height: 20px; stroke-width: 1.9; }
.dlg-cuerpo { flex: 1; min-width: 0; padding-top: 2px; }
.dlg-titulo {
  font-family: var(--font-serif);
  font-size: 18px; font-weight: 600; line-height: 1.25;
  letter-spacing: -0.2px;
  color: var(--ink);
}
.dlg-mensaje {
  font-size: 14px; line-height: 1.55;
  color: var(--ink-3);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin-top: 5px;
}
.dlg-titulo + .dlg-mensaje { margin-top: 6px; }
.dlg-acciones {
  display: flex; gap: 10px;
  margin-top: 20px;
}
.dlg-btn {
  flex: 1;
  min-height: var(--tap);
  padding: 11px 18px;
  border-radius: var(--r);
  border: 1px solid transparent;
  font-family: inherit; font-size: 15px; font-weight: 600;
  letter-spacing: -0.1px; line-height: 1.2;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s, border-color .12s, color .12s, transform .08s;
}
.dlg-btn:active { transform: scale(0.98); }
.dlg-btn-cancel {
  background: transparent;
  color: var(--ink-2);
  border-color: var(--line-2);
}
.dlg-btn-ok {
  background: var(--brand);
  color: var(--surface);
  box-shadow: 0 4px 12px var(--brand-glow);
}
@media (hover: hover) and (pointer: fine) {
  .dlg-btn-cancel:hover { background: var(--surface-2); color: var(--ink); }
  .dlg-btn-ok:hover     { background: var(--brand-ink); }
  .dlg-peligro .dlg-btn-ok:hover { background: #932E24; }
}
/* Confirmación destructiva: aquí SÍ el botón es rojo, porque la acción
   que dispara destruye algo. */
.dlg-peligro .dlg-btn-ok { background: var(--red); box-shadow: none; }

/* Tonos — solo pintan el icono */
.dlg-info  .dlg-icono { background: var(--info-bg);  color: var(--info); }
.dlg-exito .dlg-icono { background: var(--green-bg); color: var(--green-ink); }
.dlg-aviso .dlg-icono { background: var(--amber-bg); color: var(--amber); }
.dlg-error .dlg-icono { background: var(--red-bg);   color: var(--red); }

/* Desktop: tarjeta centrada, botones a la derecha con ancho natural */
@media (min-width: 640px) {
  .dlg-overlay { align-items: center; padding: 20px; }
  .dlg {
    max-width: 460px;
    border-radius: var(--r-lg);
    padding: 24px 26px;
    animation: fadeIn .18s ease-out;
  }
  .dlg-grip { display: none; }
  .dlg-acciones { justify-content: flex-end; }
  .dlg-btn { flex: 0 0 auto; min-width: 116px; }
}

/* ── Spinner / cargando ──────────────────────────────────── */
.cargando {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 48px;
  color: var(--ink-3);
  font-size: 14px; gap: 14px;
}
.spinner {
  width: 26px; height: 26px;
  border: 2px solid var(--surface-2);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: girar .65s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }

/* Spinner en línea dentro de un botón. El formulario de visita no tenía
   NINGÚN indicador de carga en 4000 líneas: todo estado ocupado era una
   sustitución de texto, y dos de ellas eran literalmente "...". */
.spinner-btn {
  width: 13px; height: 13px; flex-shrink: 0;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  opacity: .8;
  animation: girar .65s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .spinner-btn { animation: girar 1.4s linear infinite !important; }
}

/* ── Banner offline ──────────────────────────────────────── */
.offline-banner {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 9999;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 16px;
  background: var(--ink);
  color: var(--surface);
  font-size: 13px; font-weight: 600;
  font-family: var(--font-sans);
  text-align: center;
  box-shadow: var(--shadow-md);
  transform: translateY(-100%);
  transition: transform .35s cubic-bezier(.4,0,.2,1);
}
.offline-banner.visible { transform: translateY(0); }
.offline-banner .offline-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--red);
  flex-shrink: 0;
  animation: offline-pulse 2s ease-in-out infinite;
}
@keyframes offline-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .4; }
}
.offline-banner .offline-reconn {
  font-size: 11px; font-weight: 400;
  opacity: .7; margin-left: 4px;
}
body.is-offline .header {
  margin-top: 40px;
  transition: margin-top .35s cubic-bezier(.4,0,.2,1);
}


/* ╔════════════════════════════════════════════════════════════╗
   ║ 9) DESKTOP ≥ 900px                                         ║
   ╚════════════════════════════════════════════════════════════╝ */
/* 900 y no 768: el sidebar lo monta `isDesktop = winW >= 900`
   (app.jsx). Con el umbral CSS en 768 el grid reservaba su columna
   desde una tablet vertical donde nadie la ocupa — #content-desktop
   caía en ella y quedaba del ancho del sidebar, sin navegación (la
   nav inferior ya la escondía este bloque). Los dos umbrales van
   juntos: si se mueve uno, se mueve el otro. */
@media (min-width: 900px) {
  body { background: var(--paper); }

  .nav-inferior, .fab { display: none !important; }

  .fab-nueva {
    position: fixed;
    bottom: 24px; right: 24px;
    width: 56px; height: 56px;
  }

  #app-wrapper {
    max-width: 100%;
    display: grid;
    grid-template-columns: var(--sidebar-w) 1fr;
    grid-template-rows: auto 1fr;
    min-height: 100vh;
  }
  .header {
    grid-column: 1 / -1;
    padding: 10px 24px;
  }
  #sidebar-desktop {
    display: flex !important;
    flex-direction: column;
    background: rgba(255,251,245,0.75);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-right: 1px solid var(--line-2);
    padding: 16px 10px;
    position: sticky; top: var(--header-h);
    height: calc(100vh - var(--header-h));
    overflow-y: auto; gap: 2px;
  }
  .sidebar-btn {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px;
    border-radius: var(--r-sm);
    border: none; background: none;
    cursor: pointer;
    font-family: inherit;
    font-size: 14px;
    color: var(--ink-3);
    font-weight: 500;
    transition: background .12s, color .12s;
    text-align: left; width: 100%;
  }
  @media (hover: hover) and (pointer: fine) {
    .sidebar-btn:hover {
      background: var(--surface-2);
      color: var(--ink);
    }
  }
  .sidebar-btn.activo {
    background: var(--brand-bg);
    color: var(--brand);
    font-weight: 600;
  }
  .sidebar-btn svg {
    width: 18px; height: 18px;
    stroke-width: 1.6;
    flex-shrink: 0;
  }
  .sidebar-sep {
    height: 1px;
    background: var(--line);
    margin: 6px 4px;
  }

  :root { --content-pad-top: 24px; --nv-header-h: 200px; }
  #content-desktop {
    padding: var(--content-pad-top) 32px 40px;
    overflow-y: auto;
    max-height: calc(100vh - var(--header-h));
  }
  /* Aquí el scroller es #content-desktop, no el documento. Su scrollport
     es su padding box: top:0 ancla la cabecera DEBAJO del padding superior
     y esa banda queda descubierta, con el formulario asomando por ella al
     scrollear (el margen negativo de la regla base no lo evita: una caja
     sticky no colapsa márgenes). El contenedor sube hasta el borde real
     del scrollport y la cabecera se ancla por encima del padding — las
     dos cosas salen de la misma --content-pad-top que define el relleno,
     que es también la que mide el aire interior de la cabecera. */
  .pantalla.nv-pantalla { margin-top: calc(-1 * var(--content-pad-top)); }
  .nv-header { margin-top: 0; top: calc(-1 * var(--content-pad-top)); }
  body.is-offline .nv-header { top: calc(-1 * var(--content-pad-top)); }
  .pantalla {
    padding: 0;
    max-width: var(--content-max);
    margin: 0 auto;
  }
  .card { padding: 20px; }
  .form-seccion { padding: 20px; }
  .pad-bottom { padding-bottom: 20px; }
  .page-title { font-size: 30px; }

  /* Grid 2 columnas para formulario en desktop */
  .form-grid-2col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 16px;
  }
  .form-grid-2col .input-grupo { margin-bottom: 14px; }
  .form-grid-2col .input-grupo.full-width,
  .form-grid-2col .gps-box,
  .form-grid-2col textarea.input-campo {
    grid-column: 1 / -1;
  }
}

@media (min-width: 1200px) {
  :root { --sidebar-w: 272px; --content-max: 1100px; --content-pad-top: 28px; }
  #content-desktop { padding: var(--content-pad-top) 48px 48px; }
}
@media (min-width: 1600px) {
  :root { --content-max: 1200px; }
}


/* ╔════════════════════════════════════════════════════════════╗
   ║ 10) ACCESIBILIDAD — MOVIMIENTO REDUCIDO                    ║
   ╚════════════════════════════════════════════════════════════╝ */
/* QW21 (auditoría 2026-07): respeta la preferencia del SO/navegador.
   Reduce animaciones y transiciones a casi nada en vez de eliminarlas
   del todo, para no romper transiciones que comunican estado (ej. abrir/cerrar). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
