:root {
  --sand: #f4ede3;
  --adobe: #c4633f;
  --ink: #2e2a26;
  --muted: #7a6f64;
  --line: #e0d5c6;
  --card: #fffaf3;
  --erreur: #c0392b;
  --ok: #27ae60;
}

/* Mode sombre (ticket 13). Deux voies vers les mêmes valeurs : le réglage de
   l'appareil (prefers-color-scheme), SAUF si l'on a forcé le clair ; et le choix
   explicite data-theme="dark", qui gagne même sur un appareil clair. Un choix
   « clair » forcé retombe sur :root ci-dessus, d'où le :not([data-theme=light]).
   Palette chaude, pour garder l'esprit terre/adobe plutôt qu'un gris froid. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --sand: #1c1917;
    --adobe: #e08a63;
    --ink: #ece4d8;
    --muted: #a89a8a;
    --line: #3a332e;
    --card: #262220;
    --erreur: #f2a8a2;
    --ok: #7fb890;
  }
}
:root[data-theme="dark"] {
  --sand: #1c1917;
  --adobe: #e08a63;
  --ink: #ece4d8;
  --muted: #a89a8a;
  --line: #3a332e;
  --card: #262220;
  --erreur: #f2a8a2;
  --ok: #7fb890;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--sand);
  color: var(--ink);
  font: 16px/1.6 system-ui, -apple-system, "Segoe UI", sans-serif;
  display: flex;
  justify-content: center;
  padding: 1.25rem 1.25rem 4rem;
}
main { width: 100%; max-width: 34rem; }

/* La barre utilitaire, en tête de chaque page : thème à gauche, compte et
   langues poussés à droite (margin-left:auto sur le groupe). Sur une ligne dès
   qu'il y a la place ; le groupe se replie tout seul sur écran étroit. Remplace
   deux rangées empilées (langues, puis compte) et leurs marges — l'en-tête y
   gagne une pleine ligne. */
.cabecera {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem .75rem;
  margin-bottom: .75rem;
}
.hero-banner {
  background: var(--adobe);
  border-radius: .75rem;
  padding: 1.5rem 1.25rem;
  margin: 0 0 1.25rem;
}
main.pleno .hero-banner {
  margin-left: -1.25rem;
  margin-right: -1.25rem;
  border-radius: 0 0 .75rem .75rem;
}
.hero-banner-line1 {
  display: block;
  font-size: 1.25rem;
  line-height: 1.3;
  color: var(--sand);
  font-weight: 600;
}
.hero-banner-line2 {
  display: block;
  font-size: .95rem;
  line-height: 1.35;
  color: var(--sand);
  opacity: .85;
  margin-top: .15rem;
}
.hero-banner-line3 {
  display: block;
  font-size: 1rem;
  line-height: 1.35;
  color: var(--sand);
  font-weight: 500;
  margin-top: .15rem;
}
.cabecera-nav {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: .4rem .9rem;
}

.langs { display: flex; gap: .5rem; }
.langs a {
  border: 1px solid var(--line);
  color: var(--muted);
  border-radius: 999px;
  padding: .3rem .8rem;
  font-size: .8rem;
  text-decoration: none;
}
.langs a[aria-current="page"] {
  background: var(--adobe);
  border-color: var(--adobe);
  color: var(--sand);
}

/* La nav du compte, dans la barre utilitaire (voir .cabecera). Chaque pixel pris
   ici pousse « Crear mi cuenta » vers la ligne de flottaison des 360 px : d'où
   le corps réduit, comme les langues à côté. (Un passage à .95rem a vécu une
   heure le 2026-07-22 — c'était un contresens : la demande visait les TITRES
   de page .auth-h1, pas ces liens.) */
.compte { display: flex; gap: .75rem; }
.compte a { font-size: .8rem; color: var(--muted); }

/* 3.5rem : l'affiche. Ce h1 ne porte qu'un mot — « Patio » — sur l'accueil et
   sur les pages qui rendent site.name (listo, verificar). Cinq lettres tiennent
   partout, jusqu'à 360 px, et c'est le seul mot que le village retiendra : il ne
   rétrécit pas. Un clamp() global le faisait tomber à 38,7 px sur téléphone —
   mauvais échange, écarté. Les titres longs se scopent (voir .auth-h1). */
h1 {
  font-size: 3.5rem;
  letter-spacing: -.03em;
  margin: 0;
  color: var(--adobe);
}

/* Les titres de FORMULAIRE, eux, sont des phrases : « Crear una cuenta »,
   « Reenviar el enlace », « Create an account ». Mesuré à 360 px (l'Android
   d'entrée de gamme du parc d'Urubamba), à 3.5rem ils passaient à deux lignes et
   poussaient « Crear mi cuenta » sous la ligne de flottaison — le bouton même
   qu'on vient chercher sur cette page.

   Scopé, et non global : c'est le motif que .admin-h1 a posé plus bas. L'affiche
   garde ses 3.5rem ; seul ce qui ne tient pas est réduit.

   Deux causes, deux correctifs. La taille : clamp() entre 2.5rem et les 3.5rem
   d'origine, retrouvés dès ~622 px. Et le LEADING, qui coûtait plus cher que la
   taille : sans règle, ce h1 hérite du 1.6 du body, soit ~90 px par ligne. 1.1
   est ce que veut un caractère d'affiche ; 1.6 est fait pour du texte courant.

   MESURÉ SUR LA FENÊTRE, PAS SUR L'ÉCRAN — le critère retenu est le viewport de
   Chromium à 360x640 et 430x932. Un vrai téléphone rogne ~90 px de plus
   (barre d'URL, barre système) : le bouton y repasserait sous la ligne. Ça se
   scrolle, et courir après la ligne de flottaison de tous les téléphones du
   monde n'a pas de fin. Le prochain qui mesurera saura ce qui a été mesuré. */
.auth-h1 {
  /* Aligné sur .titulo-pagina (demandé, 2026-07-22) : « Mi cuenta »,
     « Entrar », « Crear una cuenta » sont des titres de PAGE comme « Avisos
     del Patio », pas des affiches — même taille, même tracking normal. Les
     clamp(2.5rem…3.5rem) d'origine venaient de l'affiche « Patio » ; à côté
     des pages de contenu ils paraissaient démesurés. */
  font-size: clamp(1.5rem, 5vw, 2.25rem);
  letter-spacing: normal;
  line-height: 1.2;
  /* Le 1.6 hérité tenait lieu d'espace sous le titre, par accident : resserrer
     le leading collait le chapeau au h1. Cette marge rend cet espace-là
     explicite, et coûte 8 px au lieu des ~90 d'une ligne fantôme. */
  margin: 0 0 .5rem;
}
/* Le titre d'une page de contenu (liste, catégories, publier…). Comme .auth-h1
   et .ficha-h1, il échappe à l'affiche « Patio » du h1 global : sans ça il
   héritait des 3.5rem et de l'interligne 1.6, un titre démesuré pour un simple
   « Avisos del Patio ». Réduit, fluide, interligne serré, sans le tracking
   négatif de l'affiche. Distinct de .auth-h1 pour ne pas toucher aux pages
   d'inscription. */
.titulo-pagina {
  font-size: clamp(1.5rem, 5vw, 2.25rem);
  line-height: 1.2;
  letter-spacing: normal;
  margin: 0 0 1rem;
  overflow-wrap: break-word;
}

.tagline { color: var(--muted); margin: .25rem 0 2.5rem; }

.badge {
  display: inline-block;
  border: 1px dashed var(--adobe);
  color: var(--adobe);
  border-radius: 999px;
  padding: .35rem 1rem;
  font-size: .8rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  margin-bottom: 1.5rem;
}

p { margin: 0 0 1.25rem; }
p a { color: var(--adobe); }
.lead { font-size: 1.125rem; }

/* Le CTA de l'accueil : un lien « Ver los avisos → » qui se lit comme un bouton,
   avec le même inversé que le bouton publier de la nav (.sitio .destacado). */
.cta { margin: 1.5rem 0; }
.cta a {
  display: inline-block;
  background: var(--sand);
  color: var(--adobe);
  padding: .55rem 1.1rem;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 600;
  border: 1px solid var(--adobe);
  transition: .15s ease;
}
.cta a:hover {
  background: var(--adobe);
  color: var(--sand);
}

ul { margin: 0 0 2rem; padding-left: 1.1rem; color: var(--muted); }
li { margin-bottom: .4rem; }

footer {
  border-top: 1px solid var(--line);
  padding-top: 1.5rem;
  color: var(--muted);
  font-size: .9rem;
}
footer p { margin: 0 0 .75rem; }
footer p:last-child { margin: 0; }
.note {
  border-left: 2px solid var(--line);
  padding-left: .9rem;
  font-size: .85rem;
}

form { display: flex; flex-direction: column; gap: .35rem; margin-bottom: 2rem; }
label { font-size: .85rem; color: var(--muted); margin-top: .75rem; }
input {
  border: 1px solid var(--line); border-radius: .4rem;
  padding: .6rem .7rem; font: inherit; background: var(--card, #fffaf3);
  color: var(--ink);
}
button {
  margin-top: 1.5rem; align-self: flex-start;
  background: var(--adobe); color: var(--sand);
  border: none; border-radius: 999px;
  padding: .6rem 1.4rem; font: inherit; cursor: pointer;
}
.erreur { color: var(--erreur); font-size: .85rem; margin: .2rem 0 0; }
.aviso { border-left: 2px solid var(--adobe); padding-left: .9rem; }
.costo-aviso { margin: .5rem 0; color: var(--muted); }
.costo-aviso.costo-gratis { color: var(--adobe); font-weight: 600; }

/* Bouton action destructive (éliminer compte, etc.) */
.eliminar-form .danger,
button.danger { background: var(--erreur); color: var(--sand); }

.guia-maj { margin: 1.5rem 0 0; font-size: .8rem; color: var(--muted); text-align: right; }

/* Le rappel en tête de l'édition vers les versions par langue (la section vit
   tout en bas, sous « Guardar ») : assez visible pour ne plus se rater, sans
   crier. Reprend l'accent adobe de .aviso. */
.traduccion-arriba { border-left: 2px solid var(--adobe); padding-left: .9rem; margin: 1rem 0; }
.traduccion-arriba a { color: var(--adobe); font-weight: 600; }

/* Le champ mot de passe à œil (_campo_password.html). L'œil vit DANS le champ,
   à droite ; l'input réserve la place pour que la frappe ne passe pas sous
   l'icône. Le bouton échappe au bouton global (fond adobe, margin-top,
   align-self) — même redressement que .acciones button et .boton-enlace. */
.campo-password { position: relative; display: flex; flex-direction: column; }
.campo-password input { padding-right: 2.6rem; }
.ver-password {
  position: absolute; right: .35rem; top: 50%; transform: translateY(-50%);
  margin: 0; padding: .3rem; align-self: auto;
  background: none; border: none; border-radius: .3rem;
  color: var(--muted); cursor: pointer; line-height: 0;
}
/* L'état vit dans aria-pressed (posé par patio-password.js) ; l'icône suit
   ici — œil quand le mot de passe est caché, œil barré quand il est montré. */
.ver-password .ojo-tachado { display: none; }
.ver-password[aria-pressed="true"] .ojo { display: none; }
.ver-password[aria-pressed="true"] .ojo-tachado { display: inline; }

/* --- Barre de progression (financement) ---------------------------------
   Piste discrète, remplissage à la couleur de la marque (--adobe), bien
   définie dans les deux thèmes. La largeur est posée en ligne par le gabarit
   (le pourcentage du bilan) ; tout le reste est ici. */
.progreso {
  height: 1.25rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: .75rem;
  overflow: hidden;
  margin: 1rem 0 .4rem;
}
.progreso-relleno {
  height: 100%;
  min-width: 2px;  /* un filet visible même à un très faible pourcentage */
  background: var(--adobe);
}

/* --- Admin des comptes --------------------------------------------------
   La page n'existe que pour la file d'attente : les comptes à confirmer sont
   en tête, et « Confirmer » est le seul bouton qui compte. */

/* 34rem suffit à une colonne de texte, pas à six colonnes de tableau. Partagé
   avec la liste des annonces : à ne pas élargir pour l'admin seule. */
main.ancho { max-width: 60rem; }
/* .pleno : toute la largeur disponible (demandé pour /admin et le monitoring).
   Le padding du body tient lieu de gouttières ; sur un grand écran les tableaux
   admin et le tableau de bord respirent au lieu d'être bridés à 60rem. */
main.pleno { max-width: none; }
/* Le h1 de l'accueil fait 3.5rem : c'est une affiche. Ceci est un outil. */
.admin-h1 { font-size: 2rem; margin-bottom: 1.5rem; }
.admin-h2 { font-size: 1.25rem; margin: 2.5rem 0 .75rem; }

/* --- Les premières annonces en relecture ---------------------------------
   En tête de page, et teintées : au bout de chaque bloc il y a quelqu'un dont
   l'annonce est invisible tant que Thierry n'a pas tranché. Le reste de la
   page est une liste de comptes qui, elle, peut attendre. */
.pendientes { margin-bottom: 1rem; }
.pendientes-h2 {
  font-size: 1.25rem;
  margin: 0 0 .5rem;
  display: flex;
  align-items: center;
  gap: .5rem;
}
/* La pastille du nombre : c'est elle qu'on voit avant d'avoir lu le titre. */
.pendientes-cuenta {
  background: var(--adobe);
  color: var(--sand);
  border-radius: 999px;
  min-width: 1.6rem;
  padding: .05rem .5rem;
  text-align: center;
  font-size: .85rem;
  font-weight: 600;
}
.pendientes-vacio, .pendientes-ayuda {
  color: var(--muted);
  font-size: .85rem;
  margin: 0 0 1rem;
}

.pendiente {
  border: 1px solid var(--adobe);
  border-left-width: 4px;
  border-radius: 4px;
  padding: .9rem 1rem;
  margin-bottom: 1rem;
}
.pendiente-titulo { font-size: 1.05rem; margin: 0 0 .4rem; }
.pendiente-meta, .pendiente-autor {
  margin: 0 0 .4rem;
  font-size: .8rem;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}
.pendiente-tipo, .pendiente-precio { color: var(--ink); font-weight: 600; }
.pendiente-email { word-break: break-all; }
/* pre-wrap : la description est du texte brut jamais passé par |safe, ses
   sauts de ligne se rendent ici et nulle part ailleurs. Même raison que
   .descripcion, mais bornée en hauteur — un spam de 5000 caractères ne doit
   pas repousser le bouton « Approuver » hors de l'écran d'un téléphone. */
.pendiente-descripcion {
  white-space: pre-wrap;
  overflow-wrap: break-word;
  font-size: .9rem;
  max-height: 12rem;
  overflow-y: auto;
  margin: 0 0 .6rem;
}
.pendiente-fotos {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-bottom: .7rem;
}
/* height:auto neutralise l'attribut height="88", qui entre dans la cascade
   comme presentational hint — sans lui les vignettes non carrées s'étirent.
   Même piège qu'à .tarjeta-foto et .fotos img. */
.pendiente-fotos img {
  width: 5.5rem;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 3px;
  background: var(--line);
}

.admin-tabla { width: 100%; border-collapse: collapse; font-size: .9rem; }
.admin-tabla th {
  text-align: left;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
  font-weight: 600;
  border-bottom: 1px solid var(--line);
  padding: .5rem .6rem;
}
.admin-tabla td {
  border-bottom: 1px solid var(--line);
  padding: .7rem .6rem;
  vertical-align: middle;
}
.col-email { word-break: break-all; }

.etiqueta {
  display: inline-block;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .1rem .55rem;
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  white-space: nowrap;
}
.etiqueta.espera { border-color: var(--adobe); color: var(--adobe); }
.etiqueta.ok { border-color: var(--ok); color: var(--ok); }
.etiqueta.admin { background: var(--adobe); border-color: var(--adobe); color: var(--sand); }
.etiqueta.baja { background: var(--muted); border-color: var(--muted); color: var(--sand); }

/* Les règles globales de form/button visent un formulaire en colonne, avec un
   bouton détaché de 1.5rem. Dans une cellule d'action, il les faut à plat :
   display:contents efface le form de la mise en page sans toucher à l'envoi. */
.acciones { display: flex; flex-wrap: wrap; gap: .4rem; }
.acciones form { display: contents; margin: 0; }
.acciones button {
  margin: 0;
  align-self: auto;
  padding: .3rem .75rem;
  font-size: .8rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--muted);
  white-space: nowrap;
}
.acciones button.principal {
  background: var(--adobe);
  border-color: var(--adobe);
  color: var(--sand);
}

/* Le bandeau d'impersonation : voyant EXPRÈS. C'est la seule trace que
   l'admin agit au nom de quelqu'un — s'il se fond dans la page, il ne
   protège plus de rien. Texte en --sand sur --adobe, la paire de la marque,
   lisible dans les deux thèmes. */
.impersonando {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  background: var(--adobe); color: var(--sand);
  padding: .5rem .75rem; border-radius: .4rem; margin-bottom: 1rem;
}
.impersonando form { margin: 0; }
/* Inversé (sand sur adobe serait invisible : le bouton global est déjà
   adobe/sand), et détaché du margin-top de 1.5rem pensé pour un formulaire
   en colonne — même redressement que .acciones button. */
.impersonando button {
  margin: 0;
  background: var(--sand);
  color: var(--adobe);
  padding: .3rem .9rem;
  font-size: .85rem;
}

/* Un bouton qui se lit comme un lien : le pseudo du tableau admin est un
   POST (voir admin/index.html), mais à l'œil c'est un nom qu'on clique.
   Le form global est en colonne avec 2rem dessous : inline et sans marge
   pour tenir dans la cellule. */
.entrar-como { display: inline; margin: 0; }
/* Tout ce que le bouton global pose (margin-top 1.5rem, align-self,
   fond adobe, padding de pilule) est neutralisé — même redressement que
   .acciones button, poussé jusqu'au texte nu souligné. */
.boton-enlace {
  margin: 0; align-self: auto;
  background: none; border: none; padding: 0;
  font: inherit; color: inherit; cursor: pointer;
  text-decoration: underline;
}

.mdp-valor {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.05rem;
  background: var(--sand);
  border: 1px dashed var(--adobe);
  border-radius: .3rem;
  padding: .1rem .5rem;
  /* Un clic sélectionne tout : il part par WhatsApp, à la main. */
  user-select: all;
}

/* Le monitoring : des listes et un tableau — les chiffres portent, le décor
   non. La barre donne l'échelle d'un coup d'œil, sans JS ni lib. */
.monitoreo { margin-bottom: 1.75rem; }
.mon-lista { list-style: none; padding: 0; display: grid; gap: .35rem; }
.mon-tabla { width: 100%; border-collapse: collapse; font-size: .9rem; }
.mon-tabla th, .mon-tabla td { text-align: left; padding: .25rem .5rem; border-bottom: 1px solid var(--line); }
.mon-barra-celda { width: 40%; }
.mon-barra { height: .8rem; background: var(--adobe); border-radius: .2rem; min-width: 1px; }
.mon-periodos a[aria-current="page"] { font-weight: 700; text-decoration: none; }

/* --- Les crédits des comptes ---------------------------------------------
   C'est ce que Thierry balaie des yeux pour repérer qui est à sec : le
   « 2 activos · 30 créditos » doit se lire sans être cherché, et le réglage
   rester à côté. */

/* tabular-nums : sans lui, les chiffres n'ont pas la même chasse et les
   colonnes ne s'alignent pas — on ne balaie plus, on lit ligne à ligne.
   nowrap : « 2 activos · 30 créditos » coupé en deux ne veut plus rien dire. */
.creditos-valor {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}

/* La cellule est un lien (la fiche du compte) : sans cette règle il rendrait
   en bleu souligné du navigateur — le site n'a pas de style global pour `a`,
   chaque lien est stylé chez lui. La couleur du site suffit à dire
   « cliquable » ; le soulignement brouillerait le balayage de la colonne, et
   le rouge d'un compte à sec (.cero, plus spécifique) doit garder le dessus. */
a.creditos-valor { color: var(--adobe); text-decoration: none; }

/* Zéro crédit n'est pas un nombre parmi d'autres : ce compte ne peut plus
   rien publier — à sec par usage, ou muselé à la main. En rouge, il se voit
   sans être lu. */
.creditos-valor.cero { color: var(--erreur); }

/* Les règles globales de form/button visent un formulaire en colonne, avec
   2rem sous lui : dans une cellule, il faut le champ et le bouton à plat. Même
   raison que .acciones form, mais display:contents est exclu ici — il ferait
   tomber le champ et le bouton dans la grille du <td> au lieu de les garder
   collés. */
.creditos-form {
  display: flex;
  gap: .3rem;
  margin: .35rem 0 0;
  align-items: center;
}
/* 4rem = quatre chiffres. Un champ pleine largeur suggérerait qu'on y écrit une
   phrase, alors qu'on y écrit « 30 ». */
.creditos-campo {
  width: 4rem;
  padding: .3rem .4rem;
  font-size: .85rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.creditos-form button {
  margin: 0;
  align-self: auto;
  padding: .3rem .6rem;
  font-size: .75rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--muted);
  white-space: nowrap;
}
/* Ce que le champ POSE (le solde absolu, zéro interdit de publier), écrit noir
   sur blanc sous chaque champ : personne ne peut le deviner. */
.creditos-ayuda {
  display: block;
  margin-top: .3rem;
  font-size: .65rem;
  line-height: 1.35;
  color: var(--muted);
  max-width: 14rem;
}

/* Un tableau sur un téléphone déborde ou devient illisible. En dessous de
   40rem, chaque compte devient une fiche, et data-label rend les en-têtes que
   thead ne porte plus. */
@media (max-width: 40rem) {
  .admin-tabla thead { display: none; }
  .admin-tabla,
  .admin-tabla tbody,
  .admin-tabla tr,
  .admin-tabla td { display: block; width: 100%; }
  .admin-tabla tr {
    border: 1px solid var(--line);
    border-radius: .5rem;
    background: var(--card);
    padding: .6rem .9rem;
    margin-bottom: .8rem;
  }
  .admin-tabla td { border: none; padding: .3rem 0; }
  .admin-tabla td::before {
    content: attr(data-label);
    display: block;
    font-size: .65rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--muted);
  }
  .acciones { margin-top: .3rem; }
  /* En fiche, la cellule occupe toute la largeur : l'aide n'a plus à se serrer
     dans une colonne de tableau. */
  .creditos-ayuda { max-width: none; }
}

/* --- Annonces ----------------------------------------------------------- */

/* La SEULE façon de rendre les sauts de ligne d'une description. Le réflexe est
   {{ desc|replace("\n","<br>")|safe }}, et |safe sur du texte d'utilisateur, sur
   un site où n'importe qui ouvre un compte, est un trou XSS béant. Ici Jinja
   échappe et le navigateur rend les retours. Un test garde les deux moitiés.
   pre-wrap et non pre : les longues lignes doivent se replier sur un téléphone.
   overflow-wrap casse les URL collées, qui déborderaient sinon. */
.descripcion {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: .8rem 0 1.2rem;
}

.ficha-tipo {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  margin: 0;
}

/* Le titre d'une annonce est du CONTENU, pas l'affiche « Patio » : il hérite
   sinon des 3.5rem du h1 global et de l'interligne 1.6 du body, ce qui donnait
   un titre géant sur plusieurs lignes très espacées (mesuré : « Fontanero
   Profesional – Reparaciones e Instalaciones de Plomería » remplissait l'écran).
   Ramené à une taille de titre, fluide, interligne serré et sans le tracking
   négatif de l'affiche. overflow-wrap pour qu'un mot à rallonge se coupe au lieu
   de déborder. Même motif scopé que .auth-h1. */
.ficha-h1 {
  font-size: clamp(1.4rem, 5vw, 2rem);
  line-height: 1.2;
  letter-spacing: normal;
  margin: .1rem 0 .4rem;
  overflow-wrap: break-word;
}

.ficha-precio {
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--adobe);
  margin: .2rem 0 0;
}

.ficha-revision { margin: .8rem 0; }

/* Les photos de la fiche.
   auto-fit + minmax : une seule photo prend toute la largeur, deux se posent
   côte à côte dès qu'il y a la place, et sur un écran de 360 px tout retombe
   en une colonne sans média-query.
   gap : sans lui, deux photos bord à bord se lisent comme une seule image.
   max-width sur l'img : une miniature de 1600 px ne doit pas pousser la page
   au-delà de l'écran — c'est la barre de défilement horizontale classique. */
.fotos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  gap: .6rem;
  margin: .8rem 0 1.2rem;
}

/* height: auto porte DEUX raisons depuis que ficha.html émet width/height, et
   la seconde est la moins évidente : les attributs entrent dans la cascade
   comme « presentational hints », donc height="896" agit comme
   `height: 896px`. Sans lui, max-width réduirait la largeur sur un téléphone
   pendant que la hauteur resterait figée — toutes les photos étirées. Avec lui,
   le navigateur déduit le rapport des deux attributs et réserve la place AVANT
   que l'image arrive : c'est ce qui empêche la fiche de sauter en 3G.
   C'est le même piège que .tarjeta-foto plus bas, mesuré au navigateur. */
.fotos img {
  max-width: 100%;
  height: auto;
  border-radius: .35rem;
}

/* Le canal (« a WhatsApp » / « sans WhatsApp ») est une PRÉCISION sur le numéro,
   pas une donnée de même rang : le numéro est ce qu'on recopie pour appeler.
   D'où le gris et le corps plus petit — sans ça, les deux se lisent d'une seule
   voix et l'œil ne sait plus lequel composer. */
.ficha-contacto-canal { color: var(--muted); font-size: .85rem; }

.ficha-datos { margin: 0 0 1.2rem; }
.ficha-datos dt {
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
  margin-top: .6rem;
}
.ficha-datos dd { margin: .1rem 0 0; }

.precio-grupo { display: flex; gap: .5rem; }
.precio-grupo input { flex: 1 1 auto; min-width: 0; }
.precio-grupo select { flex: 0 0 auto; width: auto; }

.checkbox {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-weight: 400;
}
.checkbox input { width: auto; margin: 0; }

/* Le formulaire de contact de /cuenta.
   Le h2 rouvre un titre dans un `form` qui est en display:flex column — sans
   marge propre il se collerait au champ. */
.contacto-h2 { font-size: 1rem; margin: .2rem 0 0; }

/* Même rôle que .creditos-ayuda : ce que personne ne peut deviner, écrit sous les
   yeux. Ici c'est OÙ le numéro paraît, et que vider le champ l'efface. Deux
   choses qu'on prend à quelqu'un si on ne les dit pas. */
.contacto-ayuda {
  margin: .3rem 0 0;
  font-size: .7rem;
  line-height: 1.4;
  color: var(--muted);
}

.contacto-guardado { margin: .6rem 0 0; }

/* L'aperçu du lien wa.me, sous la case. À RELIRE, pas à suivre : c'est le seul
   endroit où la personne peut voir que la règle de l'indicatif s'est trompée
   sur SON numéro. D'où le corps un peu plus grand que .contacto-ayuda et la
   couleur normale — un gris d'aide se saute des yeux, et celui-ci doit être lu.
   `word-break` parce qu'un lien ne se coupe pas aux espaces : sans lui,
   wa.me/5133612345678 déborderait de l'écran d'un Android d'entrée de gamme,
   et la fin du numéro — justement la partie fausse — sortirait du cadre. */
.contacto-enlace {
  margin: .6rem 0 0;
  font-size: .8rem;
  line-height: 1.4;
  word-break: break-word;
}

/* Le bouton « Salir » est dans son propre form, donc collé au « Guardar » du
   précédent : deux boutons voisins se cliquent l'un pour l'autre, et celui-ci
   déconnecte. La marge est ce qui les sépare. */
/* Le salir du bas, à droite comme celui du haut (demandé) : les deux sorties
   au même bord, l'œil n'en cherche qu'un. Le bouton global s'aligne à gauche
   (align-self: flex-start) — on le renvoie à droite dans ce formulaire-ci. */
.contacto-salir { margin-top: 1.6rem; }
.contacto-salir button { align-self: flex-end; }
/* La sortie en tête de /cuenta : SUR la ligne du titre, poussée à droite.
   Le formulaire perd la colonne du form global et le bouton la marge haute
   du bouton global — sinon il flotterait sous la ligne de base du titre. */
.cuenta-cabecera {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
}
.cuenta-cabecera .auth-h1 { margin-bottom: 0; }
.salir-arriba { margin: 0; }
.salir-arriba button { margin-top: 0; }


/* Une liste, pas une grille de vignettes : le public est sur un téléphone en
   3G, et il n'y a pas encore de photos. */
.lista-avisos { list-style: none; padding: 0; margin: 0 0 1.2rem; }

.tarjeta {
  border: 1px solid var(--line);
  border-radius: .5rem;
  background: var(--card);
  margin-bottom: .7rem;
}

/* Deux blocs à plat : la vignette, puis le corps qui empile tout le reste.
   Un flex plutôt qu'une grille à trois colonnes (vignette | texte | prix) : sur
   360 px, un prix long en `nowrap` — « 90.00 PEN / día » — volait ~130 px et
   étranglait le titre à ~60 px, qui tombait alors un MOT PAR LIGNE (mesuré sur
   un vrai téléphone). Ici le titre reçoit toute la largeur restante et le prix
   passe SOUS lui, sur sa propre ligne. Une carte sans photo n'a pas de bloc
   vignette du tout — le corps commence au padding, comme avant.
   min-width: 0 sur le corps est ce qui l'AUTORISE à se replier : sans lui, un
   enfant flex refuse de rétrécir sous la taille de son contenu et déborderait. */
.tarjeta-enlace {
  display: flex;
  gap: .6rem;
  padding: .7rem .9rem;
  text-decoration: none;
  color: inherit;
}
.tarjeta-cuerpo {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .1rem;
}

.tarjeta-tipo {
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}

/* La catégorie « rubrique › sous-catégorie », sous le type. En casse normale et
   un poil plus grande que le type (en majuscules) : les deux lignes ne se lisent
   pas d'une seule voix, et l'œil distingue « quel genre d'annonce » (le type) de
   « dans quelle rubrique » (la catégorie). */
.tarjeta-categoria { font-size: .75rem; color: var(--muted); }

/* overflow-wrap: break-word et non anywhere : on ne coupe un mot QUE s'il est
   plus long que la ligne (un lien collé), pas au milieu d'« Instalaciones ». */
.tarjeta-titulo { font-weight: 600; overflow-wrap: break-word; }

.tarjeta-precio {
  margin-top: .1rem;
  font-weight: 600;
  color: var(--adobe);
}

.tarjeta-lugar { font-size: .8rem; color: var(--muted); }

/* La vignette de couverture, à gauche.
   Une liste sert à PARCOURIR ; une fiche sert à montrer. Une photo pleine
   largeur dans une liste est une fiche déguisée : elle donnait une annonce et
   demie par écran de 360 px, et personne ne fait défiler quatorze annonces à ce
   rythme en 3G. 5.5rem (88 px) suffit à reconnaître l'objet.

   align-self: start (dans le flex de .tarjeta-enlace) plutôt que l'étirement
   par défaut : `stretch` imposerait la hauteur du bloc et écraserait
   l'aspect-ratio quand le titre passe sur plusieurs lignes.

   Carrée : les miniatures font 400 px sur le GRAND côté, donc un paysage sort
   en 400×300 et un portrait en 300×400. Un cadre carré + object-fit: cover les
   recadre tous les deux dans la même boîte — les titres restent alignés d'une
   carte à l'autre quelle que soit l'orientation, et rien n'est déformé. */
.tarjeta-foto {
  flex: none;              /* largeur fixe : ne rétrécit ni ne s'étire */
  align-self: start;
  width: 5.5rem;
  /* height: auto est OBLIGATOIRE, et ce n'est pas une précaution de style.
     Les attributs width/height du HTML entrent dans la cascade comme des
     « presentational hints » : height="88" agit comme `height: 88px`. Sans le
     neutraliser, la hauteur est FIGÉE par l'attribut et aspect-ratio n'est
     jamais consulté — du code mort. Mesuré au navigateur : à width: 6rem la
     boîte sortait en 96×88, plus carrée du tout. Elle ne paraissait carrée que
     parce que 5.5rem valait 88 px par coïncidence, à la taille de police par
     défaut. Quiconque grossit le texte de son téléphone — précisément ceux qui
     en ont besoin — aurait eu des vignettes déformées. */
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: .35rem;
  /* L'écart vignette↔texte est porté par le `gap` de .tarjeta-enlace. */
}

/* Les deux liens qui font exister les annonces. Ils sont à GAUCHE et non avec
   .langs / .compte à droite : ce ne sont pas des réglages, c'est ce pour quoi
   les gens viennent. Un lien noyé dans la barre du compte se serait fait
   oublier — c'est déjà arrivé au lien admin, qui existait sans que personne ne
   le voie.

   gap : le lien « Publicar » doit respirer, sinon les deux se lisent comme un
   seul mot sur un téléphone. margin-bottom faible : le commentaire de .compte
   rappelle que chaque pixel pris ici pousse le contenu sous la ligne de
   flottaison des 360 px.

   flex-wrap : les cinq pastilles + les écarts dépassent un écran de moins de
   400 px ; sans lui, « Publicar un aviso » (le dernier item) sortait de
   l'écran vers la droite. Comme .cabecera et .cabecera-nav, la nav se replie
   sur deux lignes quand la place manque. */
.sitio { display: flex; flex-wrap: wrap; gap: 1rem; align-items: baseline; margin: 0 0 1.25rem; }
.sitio a {
  font-size: .95rem;
  color: var(--sand);
  background: var(--adobe);
  padding: .3rem .7rem;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 600;
  transition: .15s ease;
}
.sitio a:hover {
  background: var(--sand);
  color: var(--adobe);
}
/* .destacado et non a.destacado : le bouton publier est un <a> quand on peut
   publier, un <span> grisé quand il ne reste plus de crédits — les deux
   portent la pastille. Le style est inversé (sand/adobe) pour le distinguer
   visuellement des autres liens nav. */
.sitio .destacado {
  background: var(--sand);
  color: var(--adobe);
  padding: .3rem .7rem;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 600;
  border: 1px solid var(--adobe);
  transition: .15s ease;
}
.sitio .destacado:hover {
  background: var(--adobe);
  color: var(--sand);
}
.sitio .deshabilitado { opacity: .5; cursor: not-allowed; }

/* Le repère « Ejemplo » sur les annonces de démonstration. En bas du corps, sur
   sa propre ligne : dernier dans le DOM, il ne décale pas le titre — et le titre
   est ce qu'on vient lire. align-self: flex-start pour que la pastille s'arrête
   au mot au lieu de barrer la carte.
   Discret exprès : il doit informer, pas crier. Ces annonces disparaîtront. */
.tarjeta-demo {
  align-self: flex-start;
  margin-top: .15rem;
  font-size: .6rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: .1rem .4rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
}

/* Sur la fiche, l'inverse : là, il faut expliquer pourquoi personne ne
   répondra. Reprend .aviso, comme le mot de la modération. */
.ficha-demo { margin-bottom: 1.2rem; }

/* --- Les annonces du compte, sur /cuenta -------------------------------- */

/* PAS .tarjeta, et ce n'est pas du goût : .tarjeta-enlace est une grille de
   TROIS colonnes (auto 1fr auto — vignette, texte, prix). Sur 360 px, la
   colonne du milieu qui porte le titre ne reçoit que ce que les deux autres
   laissent. Mesuré au navigateur, viewport réel de 360 px, avec un vrai titre
   d'Urubamba (« Cuarto amoblado con baño propio y vista al Chicón ») : le titre
   tombe à 193 px et se casse en TROIS lignes — et c'était sans vignette, la
   colonne `auto` mesurant zéro faute de photo. Une carte AVEC vignette lui en
   prend 88 de plus (non mesuré : cette page-ci n'est pas de ce ticket). Un
   titre court comme « Moto Honda » ne le montre jamais : mesurer avec le titre
   d'une fixture, c'est mesurer la fixture.

   Ici, une seule colonne. Il n'y a rien à droite du titre : ni prix (l'auteur
   connaît le sien — la question qu'il pose est « qu'est devenue mon annonce »,
   pas « combien »), ni vignette (il reconnaît la sienne au titre). Le titre
   prend donc toute la largeur : 284 px sur le même écran, deux lignes. Le
   défaut de .tarjeta sur /avisos reste entier et n'est PAS de ce ticket — cet
   écran l'évite en ne réclamant pas les colonnes, pas en corrigeant l'autre. */
.mis-avisos-seccion { margin-bottom: 2.5rem; }
.mis-avisos-h2 { font-size: 1.1rem; margin-bottom: .3rem; }

/* Le solde de crédits se lit AVANT la liste : l'ordre de lecture.mis_avisos
   met en tête ce qui paraît encore (published + pending_review, voir
   annonces_actives). */
.mis-avisos-creditos { color: var(--muted); font-size: .85rem; margin: 0 0 1rem; }
.mis-avisos-vacio { color: var(--muted); }

.mis-avisos { list-style: none; padding: 0; margin: 0 0 1rem; }

.mi-aviso {
  border: 1px solid var(--line);
  border-radius: .5rem;
  background: var(--card);
  padding: .7rem .9rem;
  margin-bottom: .7rem;
}

.mi-aviso-enlace { display: block; text-decoration: none; color: inherit; }

.mi-aviso-tipo {
  display: block;
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}

/* overflow-wrap et non un truncate : un titre long doit se LIRE en entier ici.
   C'est la page où l'on cherche « laquelle est-ce ? » parmi les siennes ; une
   ellipse y couperait justement ce qui distingue deux annonces voisines. Le mot
   à rallonge d'un lien collé passe à la ligne au lieu d'élargir la page. */
.mi-aviso-titulo { display: block; font-weight: 600; overflow-wrap: anywhere; }

/* L'état est une PASTILLE et non une ligne de texte : c'est ce qu'on vient
   chercher, et l'œil doit le trouver sans lire. inline-block pour que la
   pastille s'arrête au mot plutôt que de barrer la carte. */
.mi-aviso-estado {
  display: inline-block;
  margin: .45rem 0 0;
  padding: .1rem .5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}

/* TROIS teintes seulement, et jamais la couleur SEULE : le mot dit déjà l'état
   (« En revisión », « No aprobado »), la couleur ne fait que le devancer. Un
   daltonien lit la même page. */
.mi-aviso-estado.estado-published { border-color: var(--adobe); color: var(--adobe); }
.mi-aviso-estado.estado-pending_review { border-color: var(--adobe); color: var(--adobe); }
.mi-aviso-estado.estado-hidden { border-color: var(--erreur); color: var(--erreur); }

/* La note explique la pastille : plus petite et grise, elle en dépend, elle ne
   la concurrence pas. C'est le rapport de .ficha-contacto-canal à son numéro. */
.mi-aviso-nota { font-size: .85rem; color: var(--muted); margin: .15rem 0 0; }

/* Page desarrollo : le suivi public des demandes. Couleurs d'état et cartes
   portées ici depuis l'ancienne page autonome desarrollo.html, quand elle est
   passée sous base.html. Scopé à .board/.ticket : les .estado-* des annonces
   vivent sous .mi-aviso-estado, aucun chevauchement. */
.board {
  --propuesto: #9a8f82;
  --aceptado:  #3f7fc4;
  --en_curso:  #d38b1e;
  --terminado: #7b5bb8;
  --en_prueba: #2f9c8f;
  --entregado: #3f9142;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr));
  gap: 1.5rem;
  align-items: start;
}
.board .estado-propuesto { --state: var(--propuesto); }
.board .estado-aceptado  { --state: var(--aceptado); }
.board .estado-en_curso  { --state: var(--en_curso); }
.board .estado-terminado { --state: var(--terminado); }
.board .estado-en_prueba { --state: var(--en_prueba); }
.board .estado-entregado { --state: var(--entregado); }

.board h2 {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .08em;
  display: flex; align-items: center; gap: .5rem;
  margin: 0 0 1rem;
  color: var(--state);
}
.board h2::before {
  content: ""; width: .6rem; height: .6rem; border-radius: 50%;
  background: var(--state); flex: none;
}
.board h2 .count { color: var(--muted); font-weight: 400; letter-spacing: 0; }

.ticket {
  background: var(--card, #fffaf3);
  border: 1px solid var(--line);
  border-left: 3px solid var(--state);
  border-radius: .5rem;
  padding: 1rem 1.1rem;
  margin-bottom: .75rem;
}
.ticket h3 { margin: 0 0 .35rem; font-size: 1rem; }
.ticket p { margin: 0; color: var(--muted); font-size: .925rem; }
.ticket .from {
  display: inline-block; margin-top: .6rem;
  font-size: .7rem; letter-spacing: .04em;
  color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px;
  padding: .1rem .55rem;
}
.board .empty { color: var(--muted); font-size: .9rem; font-style: italic; margin: 0 0 .75rem; }

/* Le sélecteur de thème (ticket 13). Il vit dans la nav des langues mais est
   poussé à gauche : le bouton d'un côté, ES/FR/EN de l'autre. On montre la lune
   en clair (clic → sombre) et le soleil en sombre (clic → clair) — pilotées par
   les deux mêmes voies que la palette (média + data-theme).
   Design : pilule avec fond --card, bordure --line, icône --ink.
   Au survol/focus : fond --adobe, icône --sand, transition douce. */
.theme {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: 999px; padding: 0; cursor: pointer;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.theme:hover,
.theme:focus-visible {
  background: var(--adobe); border-color: var(--adobe); color: var(--sand);
  outline: none;
}
.theme:focus-visible {
  box-shadow: 0 0 0 2px var(--sand), 0 0 0 4px var(--adobe);
}
.theme svg { display: block; width: 18px; height: 18px; }
.theme .sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme .moon { display: none; }
  :root:not([data-theme="light"]) .theme .sun { display: block; }
}
:root[data-theme="dark"] .theme .moon { display: none; }
:root[data-theme="dark"] .theme .sun { display: block; }

/* Le filtre par rayon (catégories parentes) en haut de la liste des annonces.
   Des pastilles qui s'enroulent, l'active en adobe plein — comme le sélecteur
   de langue, en plus large. */
.filtro-cat, .filtro-zona { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 2rem; }
/* La pilule « cerca de mi zona » : même pastille que les catégories, sur sa
   propre ligne juste dessous. Remontée d'1rem pour rester collée au groupe de
   filtres plutôt que de flotter seule. */
.filtro-zona { margin-top: -1rem; }
.filtro-cat a, .filtro-zona a {
  border: 1px solid var(--line); background: transparent; color: var(--muted);
  border-radius: 999px; padding: .3rem .8rem; font-size: .85rem;
  text-decoration: none;
}
.filtro-cat a.activo, .filtro-zona a.activo {
  background: var(--adobe); border-color: var(--adobe); color: var(--sand);
}

/* Le champ de recherche, au-dessus des pastilles. L'entrée prend la place, le
   bouton garde le style adobe global mais sans sa marge haute (il est en ligne
   ici). min-width:0 laisse l'entrée rétrécir en 3G plutôt que déborder. */
/* Le compteur d'annonces réelles, sous le titre de la liste. */
.lista-header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: .5rem; margin: -.5rem 0 1.25rem; }
.lista-cuenta { color: var(--muted); font-size: .9rem; margin: 0; }

.buscador { display: flex; gap: .5rem; margin: 0 0 1rem; }
.buscador input[type="search"] {
  flex: 1; min-width: 0;
  border: 1px solid var(--line); border-radius: 999px;
  padding: .5rem .9rem; font: inherit;
  background: var(--card); color: var(--ink);
}
.buscador button { margin: 0; align-self: stretch; padding: .5rem 1.1rem; font-size: .9rem; }

/* L'arbre des catégories. Les sous-catégories sont décalées et tenues par un
   trait : c'est ce trait qui dit « ceci appartient à ça », sans puces. */
.arbol { list-style: none; padding: 0; margin: 0 0 2rem; color: var(--ink); }
.arbol-rayo { margin-bottom: 1.1rem; }
.arbol-rayo > a { font-size: 1.05rem; }
.arbol ul {
  list-style: none; padding-left: 1rem; margin: .3rem 0 0;
  border-left: 1px solid var(--line);
}
.arbol li { margin-bottom: .2rem; }
.arbol a { color: var(--adobe); text-decoration: none; }
.arbol-cuenta { color: var(--muted); font-size: .8rem; margin-left: .4rem; }

/* Les cartes (Leaflet). Hauteur fixe et non un ratio : la boîte est réservée
   AVANT que Leaflet ne s'initialise, donc la page ne saute pas quand la carte
   apparaît — même souci que les width/height des miniatures. */
.mapa {
  height: 16rem;
  border: 1px solid var(--line);
  border-radius: .5rem;
  margin: 0 0 .75rem;
}
/* Le fond de la carte pendant que les tuiles arrivent : en 3G l'attente dure,
   et un rectangle blanc en mode sombre éblouirait. */
.leaflet-container { background: var(--card); }
/* Les photos sur le formulaire d'édition : chacune avec ses boutons monter,
   descendre, retirer. La première porte l'étiquette « couverture ». */
.fotos-editar { list-style: none; padding: 0; margin: 0 0 1.5rem;
                display: flex; flex-wrap: wrap; gap: .75rem; }
.foto-editar { position: relative; }
.foto-editar img { display: block; width: 88px; height: 88px;
                   border-radius: .4rem; object-fit: cover; }
.foto-portada {
  position: absolute; top: .25rem; left: .25rem;
  background: var(--adobe); color: var(--sand);
  font-size: .65rem; padding: .05rem .35rem; border-radius: .3rem;
}
.foto-acciones { display: flex; gap: .25rem; margin-top: .25rem; }
.foto-acciones form { margin: 0; }
.foto-acciones button {
  margin: 0; align-self: auto; padding: .2rem .35rem;
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); border-radius: .3rem; cursor: pointer;
}
.foto-acciones button svg { display: block; }
.foto-acciones .foto-quitar { color: #b3261e; border-color: #e0b8b4; }
/* Le menu de langue par photo (.foto-idioma) : plus large que les trois
   boutons-icônes, il passe SOUS eux (flex-wrap) — et la carte reste à la
   largeur de sa vignette (88px) : c'est le menu qui se comprime (min-width),
   pas la grille des photos qui se déforme. font:inherit sur le select, comme
   input : le navigateur ne le donne pas tout seul. */
.foto-editar { width: 88px; }
.foto-acciones { flex-wrap: wrap; }
.foto-acciones .foto-idioma { flex-basis: 100%; }
.foto-idioma select {
  min-width: 0; font: inherit; font-size: .75rem;
  padding: .2rem; border: 1px solid var(--line); border-radius: .3rem;
  background: var(--card); color: var(--ink);
}
.foto-idioma button { font-size: .75rem; }

/* Les lignes « fichier → langue » que patio-fotos-lang.js fabrique sous le
   champ fichier : nom à gauche, menu à droite, une ligne par fichier. */
.fotos-lang { display: grid; gap: .3rem; margin-top: .4rem; }
.fotos-lang-linea {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; font-size: .85rem; color: var(--muted);
}
.fotos-lang-linea span { overflow-wrap: anywhere; }

/* Un lien qui détruit : il doit se lire comme un danger, pas comme la nav. */
.peligro-enlace { color: #b3261e; }

/* Modifier et Supprimer, sur chaque annonce de « Mis avisos ». Le gap sépare
   deux liens qui, collés, se cliqueraient l'un pour l'autre. */
.mi-aviso-acciones { display: flex; gap: 1rem; margin: .35rem 0 0; font-size: .9rem; }

.mapa-boton {
  margin: 0 0 1.5rem; align-self: flex-start;
  padding: .4rem 1rem; font-size: .85rem;
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px;
}

/* --- Grands écrans -------------------------------------------------------
   Le site est mobile-first (le public d'Urubamba est sur un téléphone en 3G),
   et le reste : ces règles n'ajoutent RIEN en dessous de 48rem. Au-delà —
   tablette, ordinateur —, on cesse seulement de gaspiller la largeur d'un grand
   écran, sans rien changer à ce que voit un téléphone. */
@media (min-width: 48rem) {
  /* La liste passe à deux colonnes. Le mobile garde son unique colonne (voir
     .lista-avisos) ; sur .pleno deux colonnes tiennent au large.
     align-items: start pour que deux cartes de hauteurs différentes ne
     s'étirent pas l'une sur l'autre ; le gap remplace la marge des cartes. */
  .lista-avisos {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: .7rem;
    align-items: start;
  }
  .lista-avisos .tarjeta { margin-bottom: 0; }

  /* La carte gagne en hauteur là où il y a la place : choisir un point à la
     souris sur un grand écran demande plus que les 16rem calés pour le pouce. */
  .mapa { height: 22rem; }

  /* L'arbre des catégories sur DEUX colonnes quand la place le permet
     (demandé). break-inside: avoid pour qu'un rayon et ses sous-catégories ne
     se coupent pas en travers de la coupure de colonne. Le mobile (< 48rem)
     garde son unique colonne. */
  .arbol { column-count: 2; column-gap: 2.5rem; }
  .arbol-rayo { break-inside: avoid; }

/* /cuenta : mobile d'abord — une colonne, annonces puis réglages.
     ≥48rem : grille 2 colonnes (1fr 30rem) — annonces auto-flow à gauche,
     réglages groupés compacts à droite. */
.cuenta-principal { display: block; }
.cuenta-avisos { display: block; }
@media (min-width: 48rem) {
  .cuenta-principal {
    display: grid;
    grid-template-columns: 1fr 30rem;
    gap: 2.5rem;
    align-items: start;
  }
  /* Les annonces en colonnes internes (auto-fit) pour remplir leur zone */
  .cuenta-avisos { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1rem; }
  .mis-avisos { display: contents; }
  .mi-aviso { break-inside: avoid; }
}

  /* /admin en colonnes : les sections dans .admin-grid passent en multi-colonnes,
     le header (admin-header) reste en pleine largeur au-dessus. */
  main.admin-columnas .admin-grid { column-count: 2; column-gap: 2.5rem; }
  main.admin-columnas .admin-grid > * { break-inside: avoid; }
  main.admin-columnas .admin-grid > .comptes-h2,
  main.admin-columnas .admin-grid > .admin-tabla { column-span: all; }

  /* Monitoreo : Vida y Salud côte à côte (demandé) — deux listes courtes
     n'ont pas besoin de deux pleines largeurs. Le tableau du trafic,
     lui, reste hors du wrapper. */
  .mon-dos-columnas {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 2rem;
    align-items: start;
  }
}

@media (min-width: 72rem) {
  /* Trois colonnes quand la place le permet. */
  .lista-avisos { grid-template-columns: repeat(3, 1fr); }
  .arbol { column-count: 3; }
  main.admin-columnas .admin-grid { column-count: 3; }
}

@media (min-width: 96rem) {
  /* Quatre colonnes sur très grands écrans. */
  .lista-avisos { grid-template-columns: repeat(4, 1fr); }
  .arbol { column-count: 4; }
  main.admin-columnas .admin-grid { column-count: 4; }
}

/* La Guía del Patio (/guia) et la section ¿Cómo funciona? de l'accueil : de
   la prose. Les dt en gras — le nom d'un champ doit se repérer en balayant —
   et les dd sans l'indentation navigateur, qui mange la largeur d'un
   téléphone. Les h2 au corps d'une section, pas d'un titre de page. */
.guia h2, .como-h2 { font-size: 1.25rem; margin: 1.75rem 0 .6rem; color: var(--adobe); }
.guia ul, .guia ol { color: var(--ink); }
.guia-campos { margin: 0 0 2rem; }
.guia-campos dt { font-weight: 700; margin-top: .8rem; }
.guia-campos dd { margin: .15rem 0 0; color: var(--muted); }

/* Contenu en colonnes sur grand écran : 2 cols ≥48rem, 3 ≥72rem.
   column-width: 25rem évite des colonnes trop étroites. break-inside: avoid
   garde les blocs (h2+ul, dl) ensemble. */
@media (min-width: 48rem) {
  .guia { column-count: 2; column-gap: 3rem; column-width: 25rem; }
}
@media (min-width: 72rem) {
  .guia { column-count: 3; column-gap: 3rem; }
}
.guia-seccion { break-inside: avoid; }

/* --- Sélecteur "par page" (listings) ---------------------------------------- */

/* Sélecteur "par page" */
.per-page-selector {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-size: .85rem;
  color: var(--muted);
}
.per-page-selector label { margin: 0; }
.per-page-selector select {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .25rem .7rem;
  font: inherit;
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
  min-width: 70px;
}

/* Navigation de pagination */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: .35rem;
  margin: 1.25rem 0 0;
  padding: .5rem 0;
}
.pagination-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.2rem;
  height: 2.2rem;
  padding: 0 .6rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  font-size: .85rem;
  text-decoration: none;
  line-height: 1;
  transition: background .15s, border-color .15s, color .15s;
}
.pagination-btn:hover:not(.disabled):not(.current) {
  background: var(--adobe);
  border-color: var(--adobe);
  color: var(--sand);
}
.pagination-btn.current {
  background: var(--adobe);
  border-color: var(--adobe);
  color: var(--sand);
  font-weight: 600;
}
.pagination-btn.disabled {
  opacity: .4;
  cursor: not-allowed;
}
.pagination-ellipsis {
  color: var(--muted);
  padding: 0 .4rem;
}

.site-footer {
  margin-top: 3rem;
  padding: 1.5rem 1rem;
  background: #1c1917;
  color: #ece4d8;
  text-align: center;
  font-size: .85rem;
  border-radius: .5rem;
  margin-left: -1rem;
  margin-right: -1rem;
}
.footer-texto {
  margin-bottom: .75rem;
}
.footer-legal {
  display: flex;
  justify-content: center;
  gap: 1.5rem;
}
.footer-legal a {
  color: #a89a8a;
  text-decoration: none;
}
.footer-legal a:hover {
  color: #e08a63;
}
