:root {
  --verde-oscuro: #064908;
  --verde: #09710D;
  --verde-medio: #009900;
  --verde-claro: #73C773;
  --verde-fondo: #E6F1E7;
  --fondo: #FAFAF8;
  --superficie: #FFFFFF;
  --borde: #E4E1D8;
  --texto: #2C2C2A;
  --texto-secundario: #5F5E5A;
  --texto-tenue: #888780;
  --radio: 12px;
  --radio-grande: 24px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--fondo);
  color: var(--texto);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  scrollbar-width: thin;
}
html::-webkit-scrollbar, body::-webkit-scrollbar { width: 6px; }
html::-webkit-scrollbar-track, body::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb, body::-webkit-scrollbar-thumb {
  background: var(--borde);
  border-radius: 999px;
}

#app {
  max-width: 420px;
  margin: 0 auto;
  min-height: 100vh;
  background: var(--superficie);
  position: relative;
  padding-bottom: 24px;
}

/* ---------- Entorno de pruebas (ES_ENTORNO_PRUEBAS, ver app.js) ----------
   Banner fijo de aviso + ocultar los enlaces reales de Ko-fi (serian
   donaciones de verdad si alguien los usa pensando que es de mentira).
   Alto real del banner medido en JS (el texto puede ocupar 1 o 2 lineas
   segun el ancho de pantalla), guardado en --alto-banner-pruebas para que
   la barra superior y el principio de cada pantalla no queden tapados. */
.banner-entorno-pruebas {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10000;
  background: #b91c1c;
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  padding: 6px 12px;
  line-height: 1.35;
}
body.entorno-pruebas #app {
  padding-top: var(--alto-banner-pruebas, 0px);
}
body.entorno-pruebas .barra-superior {
  top: var(--alto-banner-pruebas, 0px);
}
body.entorno-pruebas .enlace-kofi {
  display: none !important;
}

/* 7 de agosto de 2026 (bug real reportado por el usuario: la app seguia
   girando en el movil pese al bloqueo puesto el dia anterior). Causa: el
   bloqueo anterior (manifest.json "orientation" + screen.orientation.lock()
   en helpers.js) SOLO funciona si la app esta instalada en la pantalla de
   inicio (modo standalone) - en una pestaña normal del navegador, el
   propio navegador ignora las dos tecnicas en silencio, sin avisar de
   nada.

   7 de agosto de 2026, mismo dia, TERCER ajuste (fallo real y grave
   reportado por el usuario: "la pantalla se gira totalmente al reves").
   Los dos intentos anteriores usaban un @media (orientation:landscape)
   de SOLO CSS con una unica rotacion fija de -90 grados - ese media
   query salta igual para las DOS direcciones posibles de girar el
   movil a horizontal (a la izquierda o a la derecha), pero la
   correccion de -90 grados solo es la correcta para UNA de ellas; en la
   otra, en vez de compensar, se SUMABA a la rotacion real del movil,
   dejando la pantalla boca abajo (180 grados en vez de 0). El CSS no
   puede distinguir por si solo hacia que lado se giro - hace falta
   preguntarselo a JavaScript (screen.orientation.angle, un dato de
   solo lectura que SI funciona en cualquier navegador, a diferencia de
   .lock()) y aplicar la correccion exacta que toque en cada caso, nunca
   una fija. Ver actualizarBloqueoVisualOrientacion en helpers.js - esa
   funcion pone una de estas 3 clases en <html> segun el angulo real:
   "girado-90", "girado-270" o "girado-180" (ninguna, en portrait
   normal). Las reglas de aqui abajo son solo el CSS de cada caso, la
   decision de CUAL aplicar vive en JS. */
html.girado-90, html.girado-90 body,
html.girado-270, html.girado-270 body {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 100vh;
  height: 100vw;
  overflow: hidden;
  transition: none;
}
html.girado-90 { transform: translate(-50%, -50%) rotate(-90deg); }
html.girado-270 { transform: translate(-50%, -50%) rotate(90deg); }
/* Portrait "boca abajo" (angulo 180) - caso raro en movil, la mayoria de
   sistemas no lo permiten, pero se cubre por si acaso: aqui SI sigue
   siendo un rectangulo vertical (no hace falta intercambiar ancho/alto,
   solo darle la vuelta entera). */
html.girado-180, html.girado-180 body {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  transition: none;
}
html.girado-180 { transform: translate(-50%, -50%) rotate(180deg); }

button, input, select, textarea {
  font-family: inherit;
}

button {
  font-size: 14px;
}

/* input/select/textarea a 16px (no 14px como los botones): por debajo de
   16px, Safari/iOS e incluso Chrome/Android hacen zoom automatico al tocar
   el campo para que el texto se lea bien - y luego, en muchos moviles, ese
   zoom no se deshace del todo al salir del campo, dejando la app
   "descuadrada" hasta que el usuario hace zoom manual el mismo. Subir a
   16px evita que el navegador tenga que hacer ese zoom en primer lugar. */
input, select, textarea {
  font-size: 16px;
}

button {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  padding: 10px 14px;
  cursor: pointer;
}
button:active { transform: scale(0.98); }

button.primario {
  background: var(--verde);
  color: #fff;
  border: none;
  font-weight: 500;
}

input, textarea, select {
  width: 100%;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  padding: 8px 12px;
  background: var(--superficie);
  color: var(--texto);
}

.barra-superior {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px;
  position: sticky;
  top: 0;
  background: var(--superficie);
  z-index: 5;
  border-bottom: 1px solid var(--borde);
}

.logo-texto {
  font-weight: 700;
  font-size: 20px;
  color: var(--verde);
}

.filtro-distancia {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 16px;
  font-size: 12px;
  color: var(--texto-secundario);
}
.filtro-distancia select { width: auto; max-width: 40vw; padding: 4px 6px; font-size: 12px; }

.pestanas-vista {
  display: flex;
  gap: 4px;
  padding: 0 16px 12px;
}
.pestanas-vista button {
  flex: 1;
  padding: 8px;
  border-radius: var(--radio);
  border: 1px solid var(--borde);
  background: var(--superficie);
  color: var(--texto-secundario);
  font-size: 13px;
  font-weight: 500;
}
.pestanas-vista button.activa {
  background: var(--verde);
  border-color: var(--verde);
  color: #fff;
}

.mapa-pantalla {
  width: 100%;
  height: calc(100vh - 260px);
  min-height: 260px;
  /* 3 de agosto de 2026 (bug real reportado por el usuario: la pestaña
     Mapa se montaba encima del pie fijo de la app) - Leaflet usa
     internamente z-index muy altos para sus controles/popups (400 a
     1000, ver leaflet.css), muy por encima del z-index:20 de
     .barra-inferior. Sin "position:relative" + un z-index propio aqui,
     este contenedor no crea su propio contexto de apilamiento, asi que
     esos valores internos de Leaflet se comparaban directamente contra
     el pie fijo de la app (y ganaban). Con esto, todo lo de Leaflet queda
     encerrado dentro de este div, y el pie de la app (z-index mayor,
     fuera de este contexto) se queda siempre por encima.
  */
  position: relative;
  z-index: 1;
  overflow: hidden;
}
.mapa-pantalla .leaflet-container {
  width: 100%;
  height: 100%;
  font-family: inherit;
}
.popup-mapa { text-align: center; }
.popup-mapa img { width: 140px; height: 90px; object-fit: cover; border-radius: 8px; margin-bottom: 6px; }
.popup-mapa .nombre { font-weight: 700; font-size: 12px; margin-bottom: 2px; }
.popup-mapa a { font-size: 12px; }
.marcador-apagado { filter: grayscale(1) opacity(0.6); }
.marcador-nuevo { filter: drop-shadow(0 0 5px #22c55e) drop-shadow(0 0 5px #22c55e) saturate(1.6); }

/* 4 de agosto de 2026 (pedido por el usuario): boton de centrar en mi
   ubicacion, ver mapa.js (ControlCentrarMapa) - mismo estilo circular con
   sombra que ya usan Google Maps/apps de mapas habituales para esto,
   para que se reconozca a simple vista que sirve para eso. */
.boton-centrar-mapa {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--superficie);
  border: none;
  box-shadow: 0 1px 4px rgba(0,0,0,0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--texto-secundario);
  cursor: pointer;
  padding: 0;
  /* 8 de agosto / 12 de agosto de 2026: el boton vivio en "bottomright"
     durante un tiempo, con margenes cada vez mas grandes intentando
     esquivar el pie fijo de la app (.barra-inferior) - en algunos
     entornos (iPhone/PWA) ni sumar env(safe-area-inset-bottom) bastaba.
     12 de agosto de 2026 (definitivo): el control se movio a "topright" en
     mapa.js, lejos del pie del todo - aqui ya solo hace falta un margen
     normal, igual que el resto de la app. */
  margin: 8px;
}
.boton-centrar-mapa:active { transform: scale(0.92); }

/* 12 de agosto de 2026 (pedido por el usuario): boton flotante para subir
   el feed de golpe hasta el inicio, sin animacion - mismo estilo circular
   que el de centrar el mapa. A diferencia de aquel (un control de Leaflet,
   con su propio sistema de margenes), este es un elemento normal de la
   pagina en position:fixed - "bottom" con env(safe-area-inset-bottom) es
   aqui la forma correcta y ya probada (la misma que usa .barra-inferior),
   sin el lio de margenes que tuvo el otro boton. */
.boton-subir-feed {
  position: fixed;
  right: 12px;
  bottom: calc(70px + env(safe-area-inset-bottom, 0px));
  z-index: 20;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--superficie);
  border: none;
  box-shadow: 0 1px 4px rgba(0,0,0,0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--texto-secundario);
  cursor: pointer;
  padding: 0;
}
.boton-subir-feed:active { transform: scale(0.92); }

.lista-capturas {
  /* 12 de agosto de 2026: padding superior añadido (antes 0) - el feed
     movió la cabecera completa (logo+pestañas+filtros) a un único
     contenedor sticky (ver feed.js), que ya no lleva su propio margen
     inferior separándose de esta rejilla. */
  padding: 12px 12px 100px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.tarjeta {
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  overflow: hidden;
  background: var(--superficie);
  cursor: pointer;
}

.tarjeta .foto {
  width: 100%;
  height: 120px;
  object-fit: cover;
  background: var(--verde-fondo);
  display: block;
}

.tarjeta .info {
  padding: 8px 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.tarjeta .fila {
  display: flex;
  justify-content: space-between;
  font-size: 10px;
  color: var(--texto-secundario);
}

.tira-fotos-tarjeta {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
}

.tira-fotos-tarjeta .foto {
  flex: 0 0 100%;
  scroll-snap-align: center;
}

.badge-fotos {
  position: absolute;
  bottom: 6px;
  left: 6px;
  background: rgba(0,0,0,0.6);
  color: #fff;
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 8px;
}

.badge-recogido {
  position: absolute;
  top: 6px;
  left: 6px;
  right: 6px;
  background: var(--verde);
  color: #fff;
  font-size: 9px;
  font-weight: 500;
  padding: 3px 6px;
  border-radius: 8px;
  line-height: 1.25;
  text-align: center;
}

.estrellas { color: var(--verde); font-weight: 500; }

.barra-inferior {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 420px;
  background: var(--superficie);
  border-top: 1px solid var(--borde);
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 10px 24px calc(10px + env(safe-area-inset-bottom, 0px));
  /* 3 de agosto de 2026 (pedido por el usuario, el aislamiento de
     .mapa-pantalla no bastaba del todo): z-index subido mucho mas alto
     (9999, antes 20) - por encima con margen de sobra de CUALQUIER valor
     interno de Leaflet (que llega hasta 1000 en sus controles/popups),
     para dejar cero ambiguedad de quien pinta encima de quien.
     4 de agosto de 2026 (bug real reportado por el usuario: TODOS los
     dialogos/avisos de confirmacion de la app - confirmarConDialogo,
     pedirTextoConDialogo, mostrarAviso, los visores de foto a pantalla
     completa... - se quedaban ocultos detras del pie, no se podia
     confirmar nada). Aquellos overlays usaban z-index:1000, pensado en su
     dia solo para ganarle a Leaflet - nadie los subio cuando el pie paso
     de 20 a 9999 ese mismo dia. Se han subido todos a 10000 (ver
     helpers.js, album.js, profile.js). Regla a partir de ahora: CUALQUIER
     overlay/dialogo nuevo de pantalla completa debe llevar z-index:10000
     como minimo, nunca 1000 - por encima de este pie fijo. */
  z-index: 9999;
}

.barra-inferior-atras {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 420px;
  background: var(--superficie);
  border-top: 1px solid var(--borde);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px 24px calc(10px + env(safe-area-inset-bottom, 0px));
  z-index: 9999;
}
.nav-icono {
  background: none;
  border: none;
  padding: 8px;
  color: var(--texto-secundario);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  position: relative;
}
.nav-icono:active { transform: scale(0.92); }

/* 3 de agosto de 2026 (pedido por el usuario): el boton de "atras" solo
   (barra-inferior-atras) se veia un poco mas arriba que los iconos de los
   extremos de la barra normal de 3 iconos (barra-inferior) - esa barra
   tiene en medio el boton de camara (nav-camara), que con su margin-top
   negativo influye en como el navegador centra verticalmente el resto de
   iconos de esa fila. Se baja unos pocos pixeles a mano para que quede a
   la misma altura visual. */
#nav-atras {
  transform: translateY(4px);
}
#nav-atras:active {
  transform: translateY(4px) scale(0.92);
}

/* 1 de agosto de 2026 (pedido por el usuario): numero de conversaciones
   privadas sin leer, encima del icono de mensajes en el feed/mapa. */
.badge-no-leidos {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 8px;
  background: #b3261e;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  box-sizing: border-box;
}

.nav-camara {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--verde);
  border: 4px solid var(--superficie);
  box-shadow: 0 2px 8px rgba(0,0,0,0.18);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: -30px;
  padding: 0;
}
.nav-camara:active { transform: scale(0.95); }

.pantalla-captura {
  padding: 16px;
}

.miniaturas {
  display: flex;
  gap: 8px;
  margin: 12px 0;
}
.miniaturas img {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--borde);
}

.visor-camara {
  width: 100%;
  height: 320px;
  background: #000;
  border-radius: var(--radio);
  overflow: hidden;
  position: relative;
}
.visor-camara video { width: 100%; height: 100%; object-fit: cover; }

.boton-disparo {
  width: 70px;
  height: 70px;
  border-radius: 50%;
  background: var(--verde);
  border: 4px solid var(--superficie);
  box-shadow: 0 0 0 2px var(--verde);
  margin: 20px auto 0;
  display: block;
}
.boton-disparo.inactivo {
  background: #9ca3af;
  box-shadow: 0 0 0 2px #9ca3af;
  pointer-events: none;
}

.mapa-mini {
  width: 100%;
  height: 130px;
  border-radius: var(--radio);
  border: 1px solid var(--borde);
  overflow: hidden;
}
.mapa-mini iframe { width: 100%; height: 100%; border: 0; }
.mapa-mini .mini-leaflet { width: 100%; height: 100%; }
.mapa-mini .leaflet-control-attribution { font-size: 8px; padding: 0 4px; }

.rating {
  display: flex;
  gap: 4px;
  font-size: 24px;
  cursor: pointer;
}
.rating .estrella { color: var(--borde); }
.rating .estrella.activa { color: var(--verde); }

.mensaje {
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  padding: 8px 10px;
  margin-bottom: 8px;
}
.mensaje .autor { font-size: 11px; color: var(--texto-tenue); }
.mensaje .texto { font-size: 14px; margin: 2px 0 0; }

.avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--verde);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 500;
}

.tarjeta-estrellas {
  background: var(--verde-fondo);
  border-radius: var(--radio);
  padding: 16px;
  text-align: center;
}
.tarjeta-estrellas .cifra { font-size: 28px; font-weight: 700; color: var(--verde); }

.fila-historial {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--borde);
  font-size: 13px;
}
/* El texto (primer span) ocupa el espacio que sobra y puede partirse en
   varias lineas si hace falta; las GreenStars (segundo span, .estrellas)
   nunca encogen ni se parten - quedan siempre en su propia columna a la
   derecha, alineadas entre si en vez de saltar de sitio segun cuanto texto
   haya delante. */
.fila-historial > span:first-child {
  flex: 1 1 auto;
  min-width: 0;
}
.fila-historial .estrellas {
  flex: 0 0 auto;
  white-space: nowrap;
  text-align: right;
}

/* A partir de unas 6-8 lineas (segun cuantas lleven aviso de "borrada"/
   "sin fotos" en dos lineas) el historial se corta y hace scroll interno,
   en vez de alargar la pantalla del perfil sin limite. */
#historial {
  max-height: 320px;
  overflow-y: auto;
}
#historial .fila-historial:last-child { border-bottom: none; }

/* 17 de agosto de 2026 (pedido por el usuario): las 4 listas del panel de
   administracion (admin.js) - denuncias, GreenPro, fichas y album - se
   cortan a unas 10 filas visibles y hacen scroll interno propio, mismo
   patron ya usado arriba en #historial, en vez de alargar el panel entero
   sin limite. */
.lista-admin-acotada {
  max-height: 760px;
  overflow-y: auto;
}

.centrado { text-align: center; color: var(--texto-tenue); padding: 40px 20px; }

/* Oculta solo la barra de scroll visual (el deslizado con el dedo sigue
   funcionando igual); se usa en la tira de miniaturas una vez el objeto
   esta recogido, donde ya no aporta nada verla. */
.sin-scrollbar { scrollbar-width: none; -ms-overflow-style: none; }
.sin-scrollbar::-webkit-scrollbar { display: none; }
