/* ---------- Police : Inter (auto-hébergée) ---------- */
/* Fichier variable unique (poids 100-900) dans static/fonts/ — jamais de
   CDN externe (la CSP n'autorise que 'self', et pas de dépendance tierce).
   font-display: swap : le texte s'affiche en police système le temps du
   chargement, puis bascule. Mis en cache par le service worker avec le
   reste de la coque (voir sw.js). */

@font-face {
  font-family: "Inter";
  src: url("/static/fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Fond dégradé bleu pastel — adouci (moins saturé, plus clair) par
     rapport à l'itération précédente ; voir aussi le pseudo-élément
     body::before pour les formes floues, elles aussi assourdies. */
  --bg-a: #eef2fc;
  --bg-b: #e7ecfa;

  /* Système "verre" (glassmorphism) : centralisé ici pour ajuster tout
     l'effet en un seul endroit. --glass-blur n'est appliqué qu'aux grandes
     surfaces (cartes de logement, panneau récap, modales, barre du haut) —
     jamais aux petits éléments répétés, pour rester performant sur mobile.
     Opacité relevée (verre "subtil" plutôt que très transparent) pour des
     cartes nettement plus blanches, sans devenir un aplat opaque laiteux —
     le flou laisse toujours deviner le fond derrière. */
  --glass-bg: rgba(255, 255, 255, 0.72);
  --glass-bg-light: rgba(255, 255, 255, 0.3);
  --glass-bg-opaque: rgba(255, 255, 255, 0.85);
  --glass-border: rgba(255, 255, 255, 0.8);
  --glass-blur: 16px;
  --glass-shadow: 0 8px 24px rgba(30, 41, 59, 0.1);

  /* Base blanche sous les teintes d'accent (pastilles, badges, bandeaux) :
     plus opaque que le verre neutre pour garder ces accents lisibles et
     bien identifiables même sur le fond le plus saturé. */
  --tint-base: rgba(255, 255, 255, 0.6);

  --line: rgba(30, 41, 59, 0.12);

  --ink: #1e293b;
  --muted: #4b5875;
  --muted-2: #33405c;

  /* Couleur d'action principale de toute l'appli (boutons, cases à cocher,
     anneaux de focus, liens...) : bleu, cohérent avec le bleu déjà utilisé
     pour "Lessive" (--sky-800 a la même valeur que --primary-800). */
  --primary-700: #146aa8;
  --primary-hover: #125f97;
  --primary-800: #0b5c94;
  --primary-tint: rgba(11, 92, 148, 0.14);
  --primary-tint-strong: rgba(11, 92, 148, 0.4);
  --primary-glass: linear-gradient(var(--primary-tint), var(--primary-tint)), var(--tint-base);
  --primary-shadow: rgba(11, 92, 148, 0.28);
  --primary-shadow-active: rgba(11, 92, 148, 0.3);

  /* Vert dédié à l'état "Propre" (pastille + bandeau "tout va bien") — un
     accent distinct de la couleur d'action, jamais utilisé pour un bouton. */
  --clean-800: #0b5c33;
  --clean-tint: rgba(16, 122, 68, 0.14);
  --clean-tint-strong: rgba(16, 122, 68, 0.4);
  --clean-glass: linear-gradient(var(--clean-tint), var(--clean-tint)), var(--tint-base);

  --stone-700: #43516b;
  --stone-tint: rgba(67, 81, 107, 0.14);
  --stone-glass: linear-gradient(var(--stone-tint), var(--stone-tint)), var(--tint-base);

  --sky-800: #0b5c94;
  --sky-tint: rgba(11, 92, 148, 0.14);
  --sky-glass: linear-gradient(var(--sky-tint), var(--sky-tint)), var(--tint-base);

  --rose-600: #d81b3f;
  --rose-700: #b5122f;
  --rose-tint: rgba(216, 27, 63, 0.14);
  --rose-tint-strong: rgba(216, 27, 63, 0.4);
  --rose-glass: linear-gradient(var(--rose-tint), var(--rose-tint)), var(--tint-base);

  --radius-lg: 20px;
  --radius: 18px;
  --radius-sm: 14px;
  --radius-pill: 999px;

  --ease: 140ms ease;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* Largeur du conteneur de contenu (.screen) ET de l'en-tête
     (.topbar-inner) — un seul et même conteneur centré partagé entre les
     deux, pour que l'engrenage de l'en-tête reste toujours aligné sur le
     bord droit du contenu plutôt que sur celui de l'écran. S'élargit par
     paliers avec la largeur d'écran (voir media queries plus bas) : la
     grille KPI du tableau de bord en profite pour passer de 2×2 à 4
     colonnes généreuses sans jamais se retrouver écrasée dans un
     conteneur resté étroit. */
  --content-max: 480px;
}

@media (min-width: 768px) {
  :root {
    --content-max: 720px;
  }
}

@media (min-width: 1024px) {
  :root {
    --content-max: 1000px;
  }
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  position: relative;
  color: var(--ink);
  font-family: "Inter", system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
}

/* Les contrôles de formulaire n'héritent pas de la police du body. */
button,
input {
  font-family: inherit;
}

/* Fond : dégradé bleu pastel + quelques formes floues douces pour donner à
   l'effet verre de la matière à flouter. En pseudo-élément fixe (plutôt que
   background-attachment: fixed) pour un rendu fiable sur mobile/iOS. */
body::before {
  content: "";
  position: fixed;
  inset: -10%;
  z-index: -1;
  background: radial-gradient(circle at 18% 22%, rgba(191, 219, 254, 0.5), transparent 45%),
    radial-gradient(circle at 84% 14%, rgba(199, 210, 254, 0.45), transparent 42%),
    radial-gradient(circle at 66% 90%, rgba(216, 205, 254, 0.32), transparent 45%),
    linear-gradient(135deg, var(--bg-a) 0%, var(--bg-b) 100%);
}

.hidden {
  display: none !important;
}

/* ---------- Bandes collantes : en-têtes au défilement ---------- */
/* Les deux en-têtes (appli et landing) sont `sticky` : le contenu défile
   dessous. Tant qu'ils étaient translucides, les cartes se lisaient au
   travers et paraissaient coupées en deux. La pilule intérieure est donc
   franchement opaque, et la bande qui l'entoure se remplit dès que la page
   a bougé — en haut de page elle reste transparente pour ne pas casser la
   continuité du dégradé pastel (état piloté par updateHeaderScrollState()
   dans app.js). */


/* ---------- Barres de défilement ---------- */
/* Fines, sans piste ni flèches, intégrées au panneau plutôt qu'imposées par
   le système. Appliqué à toute zone défilante de l'appli (page, modales). */

html,
.modal-body {
  scrollbar-width: thin;
  scrollbar-color: rgba(30, 41, 59, 0.15) transparent;
}

html::-webkit-scrollbar,
.modal-body::-webkit-scrollbar {
  width: 6px;
}

html::-webkit-scrollbar-track,
.modal-body::-webkit-scrollbar-track {
  background: transparent;
}

html::-webkit-scrollbar-thumb,
.modal-body::-webkit-scrollbar-thumb {
  background-color: rgba(30, 41, 59, 0.15);
  border-radius: var(--radius-pill);
}

html::-webkit-scrollbar-thumb:hover,
.modal-body::-webkit-scrollbar-thumb:hover {
  background-color: rgba(30, 41, 59, 0.3);
}

/* ---------- Verre : classe utilitaire + grandes surfaces ---------- */
/* .glass est réutilisable telle quelle ; les composants qui sont de
   "grandes surfaces" (carte de logement, panneau récap, modale, barre du
   haut, carte de connexion) partagent la même déclaration de base. */

.glass,
.login-card,
.modal,
.topbar-inner,
.public-header-inner,
.benefit-card,
.prop-card,
.summary-card,
.kpi-card,
.welcome-card {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
}

/* Repli si backdrop-filter n'est pas supporté : fond quasi opaque plutôt
   que transparent (le flou ne se fera juste pas). */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass,
  .login-card,
  .modal,
  .topbar-inner,
  .public-header-inner,
  .benefit-card,
  .prop-card,
  .summary-card,
  .kpi-card,
  .welcome-card {
    background: rgba(255, 255, 255, 0.94);
  }
}
.topbar-inner,
.public-header-inner {
  background: #fff;
}

body.is-scrolled .topbar,
body.is-scrolled .public-header {
  background: #eff3fc;
  box-shadow: 0 1px 0 rgba(30, 41, 59, 0.06), 0 6px 18px rgba(30, 41, 59, 0.07);
}

/* ---------- Champs de saisie (verre plus opaque, pas de flou) ---------- */

.login input,
.field input,
.catalog-qty-field input,
.custom-item-row input,
.end-stay-row input,
.end-stay-config-row input,
.restock-qty,
.field select {
  width: 100%;
  padding: 12px 14px;
  font-size: 16px;
  color: var(--ink);
  background: var(--glass-bg-opaque);
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 14px;
  transition: border-color var(--ease), box-shadow var(--ease);
}

.login input:focus,
.field input:focus,
.catalog-qty-field input:focus,
.custom-item-row input:focus,
.end-stay-row input:focus,
.end-stay-config-row input:focus,
.restock-qty:focus,
.field select:focus {
  outline: none;
  border-color: var(--primary-700);
  box-shadow: 0 0 0 3px var(--primary-tint);
}

/* ---------- Landing publique (visiteur non connecté) ---------- */
/* Même langage visuel que l'appli connectée : fond dégradé pastel du body,
   cartes verre, pastilles d'icônes, police Inter. Pensée mobile d'abord —
   tout est en une colonne par défaut, les grilles ne s'ouvrent qu'à partir
   de 768px. */

.landing {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.public-header {
  position: sticky;
  top: 0;
  z-index: 30;
  padding: calc(12px + var(--safe-top)) 16px 10px;
  transition: background var(--ease), box-shadow var(--ease);
}

.public-header-inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 8px 10px 8px 14px;
  border-radius: var(--radius-pill);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  color: var(--ink);
  text-decoration: none;
}

.brand-mark {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--primary-glass);
  color: var(--primary-800);
}

.brand-mark svg {
  width: 18px;
  height: 18px;
}

.brand-name {
  font-weight: 700;
  font-size: 15px;
  white-space: nowrap;
}

.public-header-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* "Se connecter" : volontairement discret face au bouton bleu plein. */
.public-link-btn {
  min-height: 40px;
  padding: 0 12px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--muted-2);
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--ease), background var(--ease);
}

.public-link-btn:hover {
  color: var(--ink);
  background: rgba(255, 255, 255, 0.5);
}

/* Chaîné à .btn-primary, déclaré plus bas dans ce fichier : à spécificité
   égale il gagnerait et imposerait sa largeur pleine et son padding. */
.btn-primary.public-cta-btn {
  width: auto;
  flex-shrink: 0;
  min-height: 40px;
  padding: 0 16px;
  font-size: 14px;
  white-space: nowrap;
  box-shadow: 0 4px 12px var(--primary-shadow);
}

/* Écrans étroits : le nom du produit, les deux boutons et leurs marges
   réclament ~406px, bien plus que les 343px utiles d'un iPhone standard —
   d'où le chevauchement. Le nom cède la place en premier (l'icône suffit à
   identifier la marque, et le titre reste juste en dessous dans le hero). */
@media (max-width: 480px) {
  .brand-name {
    display: none;
  }
}

/* Très petits écrans (320px) : masquer le nom ne suffit plus, les deux
   boutons se resserrent à leur tour. */
@media (max-width: 360px) {
  .public-header-inner {
    padding: 8px 8px 8px 10px;
    gap: 6px;
  }

  .public-header-actions {
    gap: 2px;
  }

  .public-link-btn {
    padding: 0 8px;
    font-size: 13px;
  }

  .btn-primary.public-cta-btn {
    padding: 0 12px;
    font-size: 13px;
  }
}

.landing-main {
  flex: 1;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 8px 16px 0;
  display: flex;
  flex-direction: column;
  gap: 48px;
}

/* ---------- Hero ---------- */

.hero {
  padding: 40px 0 8px;
  text-align: center;
}

.hero-title {
  margin: 0;
  font-size: clamp(30px, 8vw, 46px);
  line-height: 1.12;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.hero-sub {
  margin: 16px auto 0;
  max-width: 34em;
  font-size: 16px;
  line-height: 1.55;
  color: var(--muted-2);
}

.btn-primary.hero-btn {
  width: auto;
  margin-top: 28px;
  min-height: 52px;
  padding: 0 28px;
  font-size: 16px;
}

/* ---------- Sections ---------- */

.landing-section-title {
  margin: 0 0 20px;
  font-size: 22px;
  font-weight: 700;
  text-align: center;
}

.benefit-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

.benefit-card {
  border-radius: var(--radius-lg);
  padding: 22px;
}

.benefit-card h3 {
  margin: 14px 0 6px;
  font-size: 16px;
  font-weight: 700;
}

.benefit-card p {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted-2);
}

/* Pastille d'icône, même construction que .kpi-icon-badge côté appli. */
.benefit-icon {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.benefit-icon svg {
  width: 22px;
  height: 22px;
}

.benefit-icon-sky {
  background: var(--sky-glass);
  color: var(--sky-800);
}

.benefit-icon-rose {
  background: var(--rose-glass);
  color: var(--rose-700);
}

.benefit-icon-primary {
  background: var(--primary-glass);
  color: var(--primary-800);
}

/* ---------- Captures du produit ---------- */
/* Cadres volontairement plats (pas de perspective 3D) : le reste de l'appli
   est en cartes plates, et une capture d'écran inclinée perd en netteté sur
   les textes fins qu'on cherche justement à montrer.
   Les <img> portent width/height : le ratio en est déduit et la place
   réservée avant le chargement, sinon le lazy loading ferait sauter la mise
   en page à l'arrivée de l'image. */

.shot {
  margin: 0;
}

.shot img {
  display: block;
  width: 100%;
  height: auto;
}

/* --- Capture large, dans une fenêtre de navigateur simplifiée --- */

.shot-browser {
  margin-top: 40px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--glass-border);
  background: rgba(255, 255, 255, 0.75);
  /* Ombre plus marquée que les cartes : c'est la pièce maîtresse de la
     page, elle doit se détacher franchement du fond pastel. */
  box-shadow: 0 24px 60px rgba(30, 41, 59, 0.18);
}

.shot-browser-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 14px;
  background: rgba(255, 255, 255, 0.85);
  border-bottom: 1px solid var(--line);
}

.shot-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(30, 41, 59, 0.16);
}

/* --- Capture mobile, dans un châssis de téléphone --- */

.showcase {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}

.showcase-text {
  min-width: 0;
}

/* Dans cette section le titre accompagne un texte : il est aligné à gauche
   comme lui, et non centré comme les titres de section isolés. */
.landing-section-title.showcase-title {
  text-align: center;
}

.showcase-text p {
  margin: 0 0 12px;
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted-2);
  text-align: center;
}

.showcase-text p:last-child {
  margin-bottom: 0;
}

.shot-phone {
  flex-shrink: 0;
  width: min(260px, 70vw);
  padding: 10px;
  border-radius: 34px;
  border: 1px solid var(--glass-border);
  background: rgba(255, 255, 255, 0.8);
  box-shadow: 0 20px 48px rgba(30, 41, 59, 0.2);
}

.shot-phone img {
  border-radius: 25px;
}

@media (min-width: 768px) {
  .shot-browser {
    margin-top: 52px;
  }

  /* Texte et téléphone côte à côte : le texte reprend un alignement à
     gauche, plus naturel en colonne. */
  .showcase {
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 48px;
    text-align: left;
  }

  .landing-section-title.showcase-title,
  .showcase-text p {
    text-align: left;
  }

  .landing-section-title.showcase-title {
    margin-bottom: 12px;
  }

  .showcase-text {
    max-width: 30em;
  }
}

/* ---------- Comment ça marche ---------- */

.step-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}

.step-card {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 18px;
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid var(--glass-border);
}

.step-number {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--primary-700);
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.step-card h3 {
  margin: 4px 0 4px;
  font-size: 16px;
  font-weight: 700;
}

.step-card p {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted-2);
}

/* ---------- Appel à l'action de fin de page ---------- */

.landing-cta {
  text-align: center;
  padding: 8px 0 16px;
}

.landing-cta h2 {
  margin: 0 0 4px;
  font-size: 22px;
  font-weight: 700;
}

/* ---------- Pied de page public ---------- */

.public-footer {
  margin-top: 48px;
  padding: 24px 16px calc(28px + var(--safe-bottom));
  border-top: 1px solid var(--line);
  text-align: center;
}

.public-footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 22px;
}

.public-footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  color: var(--muted-2);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
}

.public-footer-links a:hover {
  color: var(--primary-800);
  text-decoration: underline;
}

.public-footer-note {
  margin: 8px 0 0;
  font-size: 13px;
  color: var(--muted);
}

/* Écrans larges : les deux grilles s'ouvrent, le hero respire davantage. */
@media (min-width: 768px) {
  .benefit-grid,
  .step-list {
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
  }

  .step-card {
    flex-direction: column;
    gap: 10px;
  }

  .hero {
    padding: 64px 0 16px;
  }

  .landing-main {
    gap: 64px;
  }
}

/* ---------- Pages légales (mentions, CGU, confidentialité, contact) ---------- */
/* Pages statiques autonomes servies hors SPA : elles ne chargent que
   styles.css, d'où ces quelques règles dédiées. */

.legal-page {
  max-width: 720px;
  margin: 0 auto;
  padding: calc(32px + var(--safe-top)) 16px calc(48px + var(--safe-bottom));
}

.legal-back {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  color: var(--primary-800);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
}

.legal-back:hover {
  text-decoration: underline;
}

.legal-card {
  margin-top: 12px;
  border-radius: var(--radius-lg);
  padding: 28px;
}

.legal-card h1 {
  margin: 0 0 12px;
  font-size: 26px;
  font-weight: 700;
}

.legal-card p {
  margin: 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted-2);
}

/* ---------- Connexion ---------- */

/* `align-items: flex-start` + `margin: auto` sur la carte plutôt qu'un
   `align-items: center` : la carte reste centrée quand elle tient dans la
   fenêtre, mais en mode inscription (six champs) elle dépasse sur un
   téléphone — un centrage classique en rognerait alors le haut, hors
   d'atteinte du défilement. */
.login {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding: 24px;
}

.login-card {
  width: 100%;
  max-width: 360px;
  margin: auto;
  border-radius: var(--radius-lg);
  padding: 32px 26px;
  text-align: center;
}

.login-logo {
  margin-bottom: 8px;
  color: var(--primary-800);
}

.login-logo svg {
  width: 40px;
  height: 40px;
}

.login-card h1 {
  margin: 0 0 6px;
  font-size: 20px;
}

.login-sub {
  margin: 0 0 18px;
  color: var(--muted-2);
  font-size: 14px;
}

.login input {
  margin-bottom: 12px;
}

/* Dit une fois, sous les trois champs de profil, ce que des placeholders
   "(facultatif)" répéteraient — et qui ne tiendraient de toute façon pas
   dans un champ de 275px de large sur un téléphone. */
.login-note {
  color: var(--muted);
  font-size: 12px;
  text-align: left;
  margin: -4px 0 12px;
}

.login-error {
  color: var(--rose-700);
  font-size: 13px;
  margin: -4px 0 12px;
}

.login-toggle {
  margin-top: 10px;
}

.login-notice {
  color: var(--primary-800);
  font-size: 13px;
  margin: -4px 0 12px;
}

.link-btn {
  display: block;
  width: 100%;
  text-align: center;
  border: none;
  background: transparent;
  color: var(--primary-800);
  font-size: 13px;
  font-weight: 500;
  padding: 10px 0 0;
  cursor: pointer;
  transition: opacity var(--ease);
}

/* Neutralisé le temps d'un appel réseau (voir l'assistant de création) :
   il doit cesser de se présenter comme cliquable. */
.link-btn:disabled {
  color: var(--muted);
  cursor: not-allowed;
}

.link-btn:active {
  opacity: 0.6;
}

/* ---------- Barre du haut ---------- */

/* La barre elle-même reste pleine largeur (fond verre, bordure) ; c'est
   .topbar-inner qui porte la grille et se cale sur le même conteneur
   centré que .screen (--content-max), pour que l'engrenage à droite reste
   aligné avec le contenu au lieu de flotter à l'extrême bord de l'écran
   sur grand écran. */
/* .topbar est juste le positionnement sticky + la marge qui détache la
   barre du bord de l'écran ; .topbar-inner porte le verre, les coins
   arrondis et l'ombre — une "île" flottante plutôt qu'une barre pleine
   largeur, cadrée sur le même conteneur que le contenu (--content-max). */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  padding: calc(12px + var(--safe-top)) 16px 10px;
  transition: background var(--ease), box-shadow var(--ease);
}

.topbar-inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 8px;
}

/* Sur la fiche d'un bien, la colonne centrale porte le nom du bien plutôt
   que les onglets : ce n'est plus un bloc à centrer entre deux moitiés
   égales mais un titre qui doit manger toute la place laissée par les
   icônes, d'où l'inversion exacte des trois colonnes (voir
   updateTabbar()). */
.topbar-inner.with-title {
  grid-template-columns: auto minmax(0, 1fr) auto;
}

.topbar-left,
.topbar-right {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.topbar-left {
  grid-column: 1;
  justify-self: start;
}

.topbar-right {
  grid-column: 3;
  justify-self: end;
}

/* ---------- Onglets de navigation (Tableau de bord / Mes biens) ---------- */
/* Colonne centrale de la grille de l'en-tête (voir .topbar) : reste
   centré sur toute la largeur, quelle que soit la longueur du titre à
   gauche ou le nombre d'icônes à droite. Masqué sur la fiche d'un bien
   (remplacé par le bouton retour + le nom du bien). Zone tactile ≥ 44px. */

.tabbar {
  grid-column: 2;
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}

.tab-btn {
  min-height: 44px;
  padding: 0 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--muted-2);
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--ease), background var(--ease), box-shadow var(--ease),
    transform var(--ease);
}

.tab-btn:hover {
  color: var(--ink);
}

.tab-btn:active {
  transform: scale(0.98);
}

/* Onglet actif : pilule blanche pleine plutôt qu'un simple changement de
   couleur de texte — se détache nettement du reste de la barre. */
.tab-btn.active {
  color: var(--ink);
  background: #fff;
  box-shadow: 0 1px 4px rgba(30, 41, 59, 0.16);
}

/* ---------- Nom du bien dans la barre (fiche d'un bien) ---------- */
/* Occupe la colonne centrale à la place des onglets, aligné à gauche juste
   après le bouton retour. Un nom long est tronqué par des points de
   suspension : il cède la place, jamais les trois icônes de commande
   (retour, modifier, réglages), qui restent entièrement cliquables même à
   375px de large. */

.topbar-name {
  grid-column: 2;
  justify-self: stretch;
  min-width: 0;
  margin: 0;
  padding-left: 2px;
  font-size: 17px;
  font-weight: 700;
  line-height: 44px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Petit écran : ~160px restent au nom entre les icônes — le texte rétrécit
   plutôt que de se réduire à deux mots suivis de points de suspension. */
@media (max-width: 420px) {
  .topbar-name {
    font-size: 15px;
  }
}


/* ---------- Introduction de page (titre de section) ---------- */
/* Premier bloc du contenu défilant des deux écrans de premier niveau, voir
   pageIntro() dans app.js. Pas de fond/verre — c'est du texte, pas une
   surface. La fiche d'un bien n'en a pas : son titre est le nom du bien,
   dans la barre du haut (.topbar-name). */

/* Seul contenu du bloc intro (pas de titre concurrent : l'écran est déjà
   nommé par l'onglet actif) — fait donc office de titre de section
   (20-22px, gras) malgré l'absence de <h1>. */
.page-intro-sub {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  color: var(--ink);
}

/* Seconde ligne du tableau de bord, sous la salutation : nom de la
   conciergerie s'il est renseigné, sinon le rappel "Vue d'ensemble". Discrète
   par construction — c'est un complément au titre, pas un second titre. */
.page-intro-line {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--muted-2);
}

/* Petit élément répété (topbar, cartes d'article, lignes...) : fond
   translucide, jamais de backdrop-filter. */
.icon-btn {
  border: none;
  background: rgba(255, 255, 255, 0.32);
  color: var(--muted-2);
  font-size: 20px;
  line-height: 1;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--ease), color var(--ease), transform var(--ease);
}

.icon-btn svg {
  width: 20px;
  height: 20px;
}

/* ---------- Icônes SVG inline (remplacent les emojis) ---------- */
/* Traits fins monochromes (currentColor) insérés dans du texte fixe — voir
   icon() dans app.js. Taille alignée sur le texte porteur (1em). */

.icon-inline {
  display: inline-flex;
  align-items: center;
  vertical-align: -0.2em;
}

.icon-inline svg {
  display: block;
  width: 1em;
  height: 1em;
}

.icon-btn:hover {
  background: rgba(255, 255, 255, 0.48);
}

.icon-btn:active {
  background: rgba(255, 255, 255, 0.68);
  color: var(--primary-800);
  transform: scale(0.98);
}

/* ---------- Écran ---------- */

.screen {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 20px 16px calc(40px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 22px;
}

@media (min-width: 1024px) {
  .screen {
    gap: 28px;
    padding-top: 28px;
  }
}

/* ---------- Récapitulatif du tableau de bord ---------- */
/* Grande surface du système verre (voir la règle .glass groupée), affichée
   uniquement quand il y a du contenu (stock bas ou à laver) : quand tout va
   bien, les cartes KPI suffisent déjà à le signaler, pas de bandeau ici.
   Fond neutre identique pour les deux panneaux (pas de carte pleinement
   teintée) : seule la pastille du titre et les valeurs critiques portent
   la couleur — rouge = agir (racheter/relancer), bleu-ardoise = travail en
   cours normal, sans rouge nulle part dans le panneau "À laver". Côte à
   côte sur desktop, empilés sur mobile — voir .summary-panels. */

.summary-panels {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

@media (min-width: 768px) {
  .summary-panels {
    grid-template-columns: 1fr 1fr;
    gap: 20px;
  }

  /* La liste de courses tient toute la largeur, au-dessus des deux autres :
     c'est la seule qui s'emporte telle quelle, et ses lignes (article,
     manque, biens concernés) sont plus longues que celles des panneaux
     rangés par bien. Elle évite aussi de laisser une demi-rangée vide
     quand les trois panneaux sont présents. */
  .summary-shopping {
    grid-column: 1 / -1;
  }
}

.summary-card {
  border-radius: var(--radius-lg);
  padding: 20px;
}

.summary-block {
  /* Laisse la place de la barre du haut (titre + onglets) quand on y
     défile depuis une carte KPI cliquée sur le tableau de bord. */
  scroll-margin-top: 132px;
}

/* Rangée pleine largeur : la pastille à taille fixe, le titre sur la place
   restante. Le <h3> est un bloc, il occupe donc naturellement toute la
   largeur du panneau — aucune contrainte de largeur à lui appliquer. */
.summary-block-title {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

/* Une seule ligne, jamais haché en colonne : le titre prend la place
   restante et, s'il venait à manquer de largeur (écran très étroit, libellé
   plus long), il se coupe proprement plutôt que de passer à la ligne.
   `min-width: 0` sans quoi un élément flex refuse de descendre sous la
   largeur de son contenu et pousserait la pastille hors du panneau. */
.summary-block-label {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Commande posée au bout de la ligne de titre d'un panneau (voir l'option
   `action` de summaryBlock). Chaîné à .btn-ghost, déclaré plus bas : à
   spécificité égale, sa largeur pleine et son padding l'emporteraient et le
   bouton occuperait toute la ligne au lieu de rester une pastille à droite.
   La zone tactile descend à 36px : c'est une commande secondaire dans un
   en-tête, pas une action de premier plan. */
.btn-ghost.summary-block-action {
  width: auto;
  flex-shrink: 0;
  min-height: 36px;
  padding: 0 12px;
  font-size: 13px;
  background: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
}

.btn-ghost.summary-block-action:disabled {
  color: #78879f;
  background: rgba(226, 232, 240, 0.5);
  border-color: transparent;
  cursor: not-allowed;
}

/* Deux commandes au bout d'un titre ("Tout en lessive" et "Tout récupérer")
   n'entrent pas sur une ligne à 375px : elles se replient sous le titre
   plutôt que de le comprimer. Le libellé du titre étant en `flex: 1`, il
   pousse ce groupe à droite tant qu'il y a la place. */
.summary-block-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
  min-width: 0;
}

@media (max-width: 480px) {
  .summary-block-title {
    flex-wrap: wrap;
  }

  .summary-block-actions {
    flex-basis: 100%;
    justify-content: flex-start;
  }
}

/* Pastille circulaire teintée devant le titre de chaque panneau — même
   langage que .kpi-icon-badge. */
.summary-block-icon {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.summary-block-icon svg {
  width: 16px;
  height: 16px;
}

/* Rouge = agir : racheter (liste de courses) comme relancer (stock bas). */
.summary-alert .summary-block-icon,
.summary-shopping .summary-block-icon {
  background: var(--rose-glass);
  color: var(--rose-700);
}

.summary-laundry .summary-block-icon {
  background: var(--sky-glass);
  color: var(--sky-800);
}

.summary-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Ligne-carte : chaque bien est une petite carte dans le panneau — fond
   très légèrement teinté selon le panneau (rosé/bleuté), coins arrondis,
   chevron centré verticalement à droite DANS la carte. Le hover renforce
   la teinte. */
.summary-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  border: none;
  border-radius: 12px;
  padding: 12px 14px;
  min-height: 44px;
  text-align: left;
  cursor: pointer;
  transition: background var(--ease), transform var(--ease);
}

.summary-alert .summary-row,
.summary-shopping .summary-row {
  background: rgba(216, 27, 63, 0.05);
}

.summary-laundry .summary-row {
  background: rgba(11, 92, 148, 0.045);
}

.summary-alert .summary-row:hover,
.summary-shopping button.summary-row:hover {
  background: var(--rose-tint);
}

.summary-laundry .summary-row:hover {
  background: var(--sky-tint);
}

.summary-row:active {
  transform: scale(0.98);
}

/* Ligne de liste de courses concernant plusieurs biens : aucune fiche à
   ouvrir, donc rien qui laisse croire à un clic — ni curseur main, ni
   réaction au survol ou à l'appui, ni chevron (voir shoppingSummaryRow()). */
.summary-row-static {
  cursor: default;
}

.summary-row-static:active {
  transform: none;
}

.summary-row-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.summary-row-prop {
  font-weight: 600;
  font-size: 14px;
  color: var(--ink);
}

/* Articles en liste verticale compacte : un par ligne, plafonnée côté JS
   (voir SUMMARY_LINE_LIMIT) avec une dernière ligne "… et X autres". */
.summary-row-lines {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.summary-row-line {
  font-size: 13px;
  line-height: 1.35;
  color: var(--muted);
}

/* Quantité "2 ×" du panneau À laver : semi-bold neutre, jamais rouge. */
.summary-line-qty {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--muted-2);
}

/* Seule la valeur critique ("(2/4)") ressort en rouge — jamais tout le
   texte de la ligne, jamais dans le panneau "À laver" (ces groupes n'ont
   pas d'items critiques, voir summaryRowLinesNode() dans app.js). */
.summary-row-critical {
  color: var(--rose-700);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.summary-row-more-line {
  font-size: 12px;
  color: var(--muted);
  opacity: 0.85;
}

.summary-row-chevron {
  flex-shrink: 0;
  align-self: center;
  font-size: 18px;
  line-height: 1;
  color: var(--muted);
}

.summary-more {
  border: none;
  background: transparent;
  font-size: 13px;
  font-weight: 600;
  padding: 8px 4px;
  text-align: left;
  cursor: pointer;
  min-height: 44px;
}

.summary-alert .summary-more,
.summary-shopping .summary-more {
  color: var(--rose-700);
}

.summary-laundry .summary-more {
  color: var(--sky-800);
}

/* ---------- Cartes KPI (tableau de bord) ---------- */
/* Grande surface du système verre (voir la règle .glass groupée) : peu
   nombreuses (max 4) donc le flou reste abordable en perf. Fond neutre
   identique sur les 4 cartes (pas de teinte pleine carte) — seules la
   pastille d'icône, le chiffre et (pour l'alerte) la bordure portent la
   couleur, voir .kpi-card.kpi-* plus bas. */

.kpi-row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

/* Le passage à 4 colonnes est toujours accompagné d'un élargissement du
   conteneur (--content-max, voir :root) : jamais 4 colonnes écrasées dans
   un conteneur resté étroit — les colonnes restent des parts égales (1fr)
   qui s'étirent, leur largeur dépend seulement de --content-max, jamais du
   contenu de la carte. */
@media (min-width: 768px) {
  .kpi-row {
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
  }

  .kpi-card {
    padding: 20px 18px;
    min-height: 128px;
  }

  .kpi-value {
    font-size: clamp(36px, 5vw, 48px);
  }
}

/* Grand écran : conteneur à 1000px (--content-max), quatre tuiles hautes
   et généreuses plutôt que des cartes compactes redimensionnées — chiffres
   lisibles à deux mètres. */
@media (min-width: 1024px) {
  .kpi-row {
    gap: 20px;
  }

  .kpi-card {
    padding: 26px 24px;
    min-height: 160px;
  }

  .kpi-icon-badge {
    width: 44px;
    height: 44px;
  }

  .kpi-icon-badge svg {
    width: 22px;
    height: 22px;
  }

  .kpi-value {
    font-size: clamp(58px, 4vw, 64px);
  }

  .kpi-label {
    font-size: 15px;
  }
}

.kpi-card {
  border-radius: var(--radius-lg);
  padding: 18px 16px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 16px;
  min-height: 108px;
  text-align: left;
  cursor: pointer;
  transition: background var(--ease), transform var(--ease), border-color var(--ease);
}

.kpi-card:hover {
  background: #fff;
}

.kpi-card:active {
  transform: scale(0.98);
}

.kpi-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
}

/* Le libellé peut passer sur deux lignes ("Stock bas linge" à 375px, où il
   reste ~85px à côté de la pastille) : les quatre cartes d'une rangée de
   grille gardent la même hauteur et les chiffres restent alignés en bas,
   grâce au justify-content: space-between de la carte. */
.kpi-label {
  font-size: 14px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--muted-2);
}

/* Pastille circulaire teintée derrière l'icône — toujours présente (⚠ ou
   ✓ pour la carte "stock bas"), taille fixe pour que sa présence ne
   décale jamais la position du chiffre en dessous. */
.kpi-icon-badge {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--stone-glass);
  color: var(--stone-700);
}

.kpi-icon-badge svg {
  width: 18px;
  height: 18px;
}

.kpi-value {
  font-size: clamp(40px, 10vw, 52px);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.kpi-card.kpi-alert {
  border-color: var(--rose-tint-strong);
}
.kpi-card.kpi-alert .kpi-value {
  color: var(--rose-700);
}
.kpi-card.kpi-alert .kpi-icon-badge {
  background: var(--rose-glass);
  color: var(--rose-700);
}

.kpi-card.kpi-ok {
  border-color: var(--clean-tint-strong);
}
.kpi-card.kpi-ok .kpi-value {
  color: var(--clean-800);
}
.kpi-card.kpi-ok .kpi-icon-badge {
  background: var(--clean-glass);
  color: var(--clean-800);
}

.kpi-card.kpi-stone .kpi-value {
  color: var(--ink);
}
.kpi-card.kpi-stone .kpi-icon-badge {
  background: var(--stone-glass);
  color: var(--stone-700);
}

.kpi-card.kpi-sky .kpi-value {
  color: var(--sky-800);
}
.kpi-card.kpi-sky .kpi-icon-badge {
  background: var(--sky-glass);
  color: var(--sky-800);
}

/* ---------- En-tête de page (titre + action principale) ---------- */

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* Pilule compacte, pas le gros bouton pleine largeur de .btn-primary : il
   faut donc neutraliser explicitement sa largeur, son min-height et son
   padding. Sélecteur volontairement chaîné (.btn-primary.page-header-btn) :
   .btn-primary est déclaré plus bas dans ce fichier, une simple classe
   .page-header-btn perdrait l'arbitrage à spécificité égale. */
.btn-primary.page-header-btn {
  width: auto;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 16px;
  font-size: 14px;
  white-space: nowrap;
  box-shadow: 0 4px 12px var(--primary-shadow);
}

/* Au pointeur (desktop), la contrainte de zone tactile 44px ne s'applique
   plus : le bouton retrouve la hauteur compacte de la maquette. */
@media (min-width: 768px) {
  .btn-primary.page-header-btn {
    min-height: 36px;
    padding: 0 14px;
  }
}

/* ---------- Cartes de biens (page "Mes biens") ---------- */
/* Deux colonnes sur desktop, une seule en dessous — chaque carte est une
   fiche autonome : en-tête (pastille + nom + ville + badge d'alerte),
   métadonnées, détail du linge repliable, puis les actions en pied. */

.prop-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}

@media (min-width: 768px) {
  .prop-grid {
    grid-template-columns: 1fr 1fr;
    gap: 20px;
  }
}

/* Le contenu se resserre naturellement : pas de margin-top: auto sur le
   pied, qui concentrerait tout l'espace libre en un seul bloc vide juste
   avant les boutons (visible sur une carte sans linge ou repliée). La
   rangée de la grille étire toujours les cartes à la même hauteur, mais
   space-between répartit alors ce surplus entre tous les blocs. */
.prop-card {
  border-radius: var(--radius-lg);
  padding: 18px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 14px;
  transition: background var(--ease);
}

.prop-card:hover {
  background: #fff;
}

.prop-card-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

/* Pastille ronde d'icône, même langage que .kpi-icon-badge du tableau de
   bord. */
.prop-card-badge {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--primary-glass);
  color: var(--primary-800);
}

.prop-card-badge svg {
  width: 20px;
  height: 20px;
}

.prop-card-title {
  flex: 1;
  min-width: 0;
}

.prop-name {
  font-weight: 700;
  font-size: 16px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.prop-city {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 2px;
  font-size: 13px;
  color: var(--muted);
  min-width: 0;
}

.prop-city-icon {
  flex-shrink: 0;
  display: flex;
  color: var(--muted);
}

.prop-city-icon svg {
  width: 14px;
  height: 14px;
}

.prop-city-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Ancre du menu "⋯" (voir .prop-menu) : le menu se positionne sous le
   bouton, pas au milieu de la carte dont la hauteur varie. */
.prop-card-head-actions {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.prop-menu-btn {
  font-size: 20px;
  width: 36px;
  height: 36px;
}

.prop-card-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 18px;
  font-size: 13px;
  color: var(--muted);
}

.prop-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.prop-meta-item svg {
  width: 15px;
  height: 15px;
}

/* ---------- Accès aux articles d'un bien ---------- */
/* Une ligne qui ouvre une modale, et non plus un dépliage sur place : les
   cartes étant alignées en grille, en déplier une creusait un vide de même
   hauteur dans sa voisine. La carte garde désormais une hauteur régulière et
   la grille ne bouge plus. */

/* Remplace la ligne d'accès quand le bien n'a encore aucun article : du
   texte, pas une surface, d'où le filet qui le détache de la rangée de
   métadonnées — la ligne d'accès, elle, porte sa propre bordure. */
.prop-items-empty {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--muted);
}

.prop-items-add {
  min-height: 36px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--primary-800);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity var(--ease);
}

.prop-items-add:hover {
  text-decoration: underline;
}

.prop-items-add:active {
  opacity: 0.6;
}

/* Même facture que "Gérer le bien" juste en dessous : coins arrondis, fond
   blanc adouci, bordure discrète, 44px de haut. `border` doit être déclarée
   explicitement — sans elle, un <button> hérite de la bordure du navigateur,
   épaisse, sombre et à angles droits, qui le faisait passer pour un champ de
   saisie au milieu d'une carte tout en rondeurs. */
.prop-items-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.55);
  color: var(--muted-2);
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background var(--ease), color var(--ease), transform var(--ease);
}

.prop-items-link:hover {
  background: #fff;
  color: var(--ink);
}

.prop-items-link:active {
  transform: scale(0.98);
}

/* Chevron vers la droite : c'est une ouverture, plus un dépliage. */
.prop-items-link-chevron {
  flex-shrink: 0;
  font-size: 16px;
  line-height: 1;
  color: var(--muted);
}

/* ---------- Pied de carte : actions ---------- */

.prop-card-footer {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

.prop-manage-btn {
  flex: 1;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.55);
  color: var(--muted-2);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--ease), color var(--ease), transform var(--ease);
}

.prop-manage-btn:hover {
  background: #fff;
  color: var(--ink);
}

.prop-manage-btn:active {
  transform: scale(0.98);
}

/* Écran étroit : le badge d'alerte se réduit à son icône et son nombre pour
   laisser respirer le nom du bien. Le pied ne contient plus qu'un bouton
   pleine largeur — les gestes de tournée ont leur propre rangée en dessous
   (voir .prop-quick-actions). */
@media (max-width: 420px) {
  .badge-alert-label {
    display: none;
  }

  .page-header-btn {
    padding: 12px 16px;
  }
}

/* Menu contextuel du bouton "⋯" : petite carte verre positionnée par
   rapport à .prop-card-head-actions (position: relative), comme si elle
   émanait du bouton — coin haut-droit aligné, apparition scale + fade. */
.prop-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 20;
  min-width: 170px;
  background: var(--glass-bg-opaque);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--glass-shadow);
  padding: 6px;
  transform-origin: top right;
  animation: prop-menu-in 140ms ease;
}

@keyframes prop-menu-in {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.prop-menu-item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 44px;
  padding: 10px 14px;
  border: none;
  background: transparent;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: background var(--ease);
}

.prop-menu-item:hover {
  background: rgba(255, 255, 255, 0.5);
}

.prop-menu-item:active {
  background: rgba(255, 255, 255, 0.75);
}

.prop-menu-item.danger {
  color: var(--rose-700);
}

/* ---------- Accueil premier bien (aucun logement) ---------- */
/* Grande surface du système verre (voir la règle .glass groupée) : c'est le
   seul contenu de l'écran quand le compte n'a encore aucun bien, sur le
   tableau de bord comme sur "Mes biens". Elle remplace l'ancien .empty-state
   sans fond, qui laissait un nouveau compte devant une page blanche. */

.welcome-card {
  border-radius: var(--radius-lg);
  padding: 36px 24px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

@media (min-width: 768px) {
  .welcome-card {
    padding: 48px 32px;
  }
}

/* Pastille d'icône, même langage que .kpi-icon-badge et .summary-block-icon,
   en plus grand — c'est le seul élément graphique de l'écran. */
.welcome-badge {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--primary-glass);
  color: var(--primary-800);
  margin-bottom: 4px;
}

.welcome-badge svg {
  width: 28px;
  height: 28px;
}

.welcome-title {
  margin: 0;
  font-size: 21px;
  font-weight: 700;
}

.welcome-text {
  margin: 0 0 8px;
  max-width: 380px;
  color: var(--muted-2);
  font-size: 14px;
  line-height: 1.5;
}

/* Chaîné pour la même raison que .btn-primary.page-header-btn : sans ça, le
   width: 100% de .btn-primary (déclaré plus bas) l'emporte et le bouton
   s'étale sur toute la largeur au lieu de rester centré. */
.btn-primary.welcome-btn {
  width: auto;
  padding: 14px 28px;
}

/* Les trois étapes reprennent telles quelles .step-list/.step-card de la
   landing (y compris son passage en trois colonnes à 768px) : même geste
   expliqué, même présentation. Seul l'écart au-dessus est propre à l'appli,
   où la carte d'accueil les précède. */
.onboarding-steps {
  margin-top: 4px;
}

/* Action secondaire en bas de liste : même facture que "Gérer le bien" sur
   les cartes de "Mes biens" (surface blanche, bord franc, coins doux), plutôt
   que l'ancienne tuile en pointillés qui n'avait plus d'équivalent ailleurs. */
.add-tile {
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  color: var(--muted-2);
  min-height: 48px;
  padding: 0 18px;
  font-size: 14px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  cursor: pointer;
  transition: background var(--ease), color var(--ease), transform var(--ease);
}

.add-tile:hover {
  background: #fff;
  color: var(--ink);
}

.add-tile:active {
  transform: scale(0.98);
}

/* ---------- Actions rapides d'un bien ---------- */
/* Les trois gestes de tournée, présentés à l'identique sur la carte d'un
   bien et sur sa fiche. Tuile compacte : icône au-dessus du libellé, ce qui
   la garde étroite — trois tiennent alors dans la largeur d'une carte à
   375px (97px chacune), là où trois libellés en ligne ne passaient pas.
   Volontairement secondaires : sur la carte, c'est "Gérer le bien" qui reste
   l'affordance principale ; sur la fiche, la tuile "Fin de séjour". */

.prop-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.prop-quick-actions {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

.quick-action {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 60px;
  padding: 8px 6px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.55);
  color: var(--muted-2);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition: background var(--ease), color var(--ease), transform var(--ease);
}

.quick-action-icon {
  display: flex;
  color: inherit;
}

.quick-action-icon svg {
  width: 18px;
  height: 18px;
}

.quick-action:hover {
  background: #fff;
  color: var(--ink);
}

.quick-action:active {
  transform: scale(0.98);
}

/* Le geste principal : teinté, pour se détacher des deux autres sans pour
   autant devenir un bouton plein — il reste dans la même rangée. */
.quick-action-primary {
  border-color: var(--primary-tint-strong);
  background: var(--primary-glass);
  color: var(--primary-800);
}

.quick-action-primary:hover {
  background: var(--primary-tint);
  color: var(--primary-800);
}

/* Rien à transférer : aplat gris sans bordure et curseur explicite, comme
   les boutons de transfert désactivés d'une carte d'article. */
.quick-action:disabled {
  background: rgba(226, 232, 240, 0.5);
  border-color: transparent;
  color: #78879f;
  cursor: not-allowed;
}

/* Fond plein plutôt que translucide : posé à même le dégradé de la page,
   pas sur une carte, il a besoin de son propre contraste. Sa hauteur suit
   celle des tuiles plutôt qu'un 44px figé qui le laisserait plus court. */
.end-stay-config-btn {
  flex-shrink: 0;
  align-self: stretch;
  width: 48px;
  height: auto;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--glass-border);
}

.end-stay-hint {
  margin: 0;
  font-size: 13px;
  line-height: 20px;
  color: var(--muted-2);
}

/* ---------- Récapitulatifs de mouvements ---------- */
/* Confirmations et résultats partagent la même présentation : total mis en
   avant, puis une liste — nom à gauche, quantité à droite, filet très léger
   entre les lignes. Remplace les énumérations en phrase continue, illisibles
   dès quelques articles. */

.recap-total {
  margin: 0 0 12px;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
}

.recap-group + .recap-group {
  margin-top: 14px;
}

/* Intitulé de groupe (nature, ou bien pour l'action globale) : discret, il
   sépare sans concurrencer les lignes qu'il coiffe. */
.recap-group-label {
  margin: 0 0 2px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.recap-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
}

/* Dernière ligne d'un groupe : pas de filet, il pendrait dans le vide. */
.recap-group .recap-row:last-child {
  border-bottom: none;
}

.recap-row-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.recap-row-name {
  font-size: 14px;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.recap-row-note {
  font-size: 12px;
  color: var(--muted);
}

/* Quantité alignée à droite, chiffres à chasse fixe pour que la colonne
   reste droite d'une ligne à l'autre. */
.recap-row-qty {
  flex-shrink: 0;
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-align: right;
}

/* Stock insuffisant : la ligne le dit d'elle-même (quantité obtenue en
   rouge, écart en note) plutôt qu'une phrase d'avertissement séparée. */
.recap-row-short .recap-row-qty,
.recap-row-short .recap-row-note {
  color: var(--rose-700);
}

.recap-row-short .recap-row-note {
  font-weight: 600;
}

/* Longue liste : elle devient sa propre zone défilante. Le total au-dessus
   et le bouton d'action (dans le pied fixe de la modale) restent visibles.
   Voir RECAP_SCROLL_THRESHOLD dans app.js. */
.recap-list-scroll {
  max-height: 46vh;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(30, 41, 59, 0.15) transparent;
}

.recap-list-scroll::-webkit-scrollbar {
  width: 6px;
}

.recap-list-scroll::-webkit-scrollbar-thumb {
  background-color: rgba(30, 41, 59, 0.15);
  border-radius: var(--radius-pill);
}

/* En-tête d'un résultat : pastille verte et titre court. */
.recap-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}

.recap-head-badge {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--clean-glass);
  color: var(--clean-800);
}

.recap-head-badge svg {
  width: 18px;
  height: 18px;
}

.recap-head-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
}

.recap-empty {
  margin: 0;
  font-size: 14px;
  color: var(--muted-2);
}

/* ---------- Retour de courses ---------- */
/* Même liste que les récapitulatifs, avec une case par ligne et une quantité
   ajustable : on ne rachète pas toujours tout ce que la liste annonçait. */

.restock-all {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  margin-bottom: 6px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}

.restock-check {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
}

/* Case cochée par défaut : on arrive ici en ayant fait les courses, le cas
   courant est de tout valider. Même dessin que le catalogue de l'assistant,
   en un peu plus petit — ces lignes sont plus denses. */
.restock-all input[type="checkbox"],
.restock-check input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  border-radius: 7px;
  background: rgba(255, 255, 255, 0.42);
  border: 1px solid var(--glass-border);
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}

.restock-all input[type="checkbox"]:checked,
.restock-check input[type="checkbox"]:checked {
  background-color: var(--primary-700);
  border-color: var(--primary-700);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 13 10 18 19 7'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 14px 14px;
}

.restock-all input[type="checkbox"]:focus-visible,
.restock-check input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.restock-row {
  min-height: 44px;
}

.restock-qty {
  width: 72px;
  flex: none;
  padding: 8px 10px;
  border-radius: 10px;
  text-align: center;
}

/* Ligne décochée : elle reste lisible mais s'efface, pour qu'on voie d'un
   coup d'œil ce qui part réellement au stock. */
.restock-row-off .recap-row-name {
  color: var(--muted);
}

.restock-row-off .restock-qty {
  opacity: 0.5;
}

/* Fin de séjour : seule liste dont la quantité est saisissable — le champ
   prend la place du nombre, à droite comme lui. */
.end-stay-row input {
  width: 72px;
  flex: none;
  padding: 9px 10px;
  border-radius: 10px;
  text-align: center;
}

.end-stay-config-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.end-stay-config-name {
  flex: 1;
  min-width: 0;
  font-size: 14px;
}

.end-stay-config-row input {
  width: 72px;
  flex: none;
  padding: 9px 10px;
  border-radius: 10px;
  text-align: center;
}


/* ---------- Sections d'une fiche de bien (Linge / Consommables) ---------- */
/* Même mécanique de dépliage que le détail des cartes de "Mes biens" :
   grid-template-rows 0fr → 1fr, qui s'anime vers la hauteur réelle sans
   max-height arbitraire. Ici les sections sont ouvertes par défaut — les
   articles sont le contenu de la page, pas un complément. */

.prop-section {
  display: flex;
  flex-direction: column;
}

.prop-section-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 48px;
  padding: 0;
  border: none;
  background: transparent;
  text-align: left;
  cursor: pointer;
  color: var(--ink);
  transition: color var(--ease);
}

.prop-section-head:hover {
  color: var(--primary-800);
}

.prop-section-badge {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--primary-glass);
  color: var(--primary-800);
}

.prop-section-badge svg {
  width: 16px;
  height: 16px;
}

.prop-section-title {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.prop-section-label {
  font-size: 16px;
  font-weight: 700;
}

.prop-section-count {
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}

.prop-section-chevron {
  flex-shrink: 0;
  display: flex;
  color: var(--muted);
  transition: transform 200ms var(--ease);
}

.prop-section-chevron svg {
  width: 18px;
  height: 18px;
}

.prop-section.open .prop-section-chevron {
  transform: rotate(180deg);
}

.prop-section-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 200ms ease;
}

.prop-section-wrap.open {
  grid-template-rows: 1fr;
}

.prop-section-body {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Un peu d'air une fois la section dépliée, sans en laisser quand elle est
   repliée (la marge vit à l'intérieur de la zone masquée). */
.prop-section-wrap.open .prop-section-body {
  padding-top: 4px;
}

/* Section sans aucun article : une invitation discrète plutôt qu'un bloc
   vide qui occuperait l'écran pour rien. */
.prop-section-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  padding: 0 18px;
  border: 1px dashed var(--glass-border);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.35);
  color: var(--muted-2);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--ease), color var(--ease), transform var(--ease);
}

.prop-section-empty:hover {
  background: rgba(255, 255, 255, 0.6);
  color: var(--ink);
}

.prop-section-empty:active {
  transform: scale(0.98);
}

/* ---------- Groupes par nature dans les modales de séjour ---------- */

.end-stay-group + .end-stay-group {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.end-stay-group-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 700;
  color: var(--muted-2);
}

.end-stay-group-icon {
  flex-shrink: 0;
  display: flex;
  color: var(--primary-800);
}

.end-stay-group-icon svg {
  width: 15px;
  height: 15px;
}

/* ---------- Réapprovisionnement ---------- */

.restock-preview {
  font-weight: 600;
  color: var(--ink);
}

/* ---------- Carte article ---------- */
/* Même blanc et même ombre douce que les cartes de "Mes biens", mais
   volontairement SANS backdrop-filter : ces cartes sont répétées une fois
   par article, et le flou sur un élément répété coûte cher sur mobile (voir
   la note en tête de fichier). Le fond est donc directement plus opaque
   plutôt que flouté. */

.item-card {
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--glass-shadow);
  padding: 18px;
}

/* Stock bas : bordure franche et colorée, ne se fond jamais dans le verre —
   cette alerte doit sauter aux yeux. */
.item-card.low {
  border: 2px solid var(--rose-tint-strong);
}

.item-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 14px;
}

.item-head-text {
  min-width: 0;
}

.item-name {
  font-weight: 700;
  font-size: 16px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.item-threshold {
  font-size: 13px;
  color: var(--muted);
}

.item-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}

/* 36px comme le menu des cartes de "Mes biens" : à 380px, deux boutons de
   44px plus la pastille "Stock bas" ne laisseraient plus de place au nom. */
.item-actions .icon-btn {
  width: 36px;
  height: 36px;
}

.item-actions .icon-btn svg {
  width: 17px;
  height: 17px;
}

/* Même pastille que le badge d'alerte des cartes de "Mes biens". */
.chip-low {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--rose-700);
  background: var(--rose-glass);
  padding: 4px 9px;
  border-radius: var(--radius-pill);
  margin-right: 4px;
  white-space: nowrap;
}

.chip-low .icon-inline svg {
  width: 14px;
  height: 14px;
}

/* Écran étroit : la pastille se réduit à son icône pour laisser le nom de
   l'article respirer à côté des deux boutons d'action. */
@media (max-width: 420px) {
  .chip-low-label {
    display: none;
  }

  .chip-low {
    padding: 4px 6px;
  }
}

/* Grille de trois colonnes strictement égales. `minmax(0, 1fr)` et non
   `1fr` : sans le minimum à zéro, une colonne ne peut pas devenir plus
   étroite que son contenu (deux boutons + le compteur), et la rangée
   débordait de l'écran sur mobile — le "+" de Lessive passait hors cadre. */
.pills {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-bottom: 12px;
}

/* Consommable : un seul compteur ("En stock"), qui prend toute la largeur
   de la carte au lieu d'en occuper le tiers gauche. Les boutons − et +
   partent alors aux deux extrémités (.pill-row est déjà en
   space-between), le chiffre restant centré entre les deux. */
.pills-single {
  grid-template-columns: minmax(0, 1fr);
}

.pills-single .pill {
  padding: 10px 14px;
}

.pills-single .pill-row {
  gap: 12px;
}

/* Pastilles Propre/Sale/Lessive : verre légèrement teinté, accent distinct
   par état pour rester identifiables d'un coup d'œil. Petits éléments
   répétés → pas de backdrop-filter. */
.pill {
  min-width: 0;
  border-radius: var(--radius-sm);
  padding: 8px 6px;
  border: 1px solid transparent;
}

.pill.emerald {
  background: var(--clean-glass);
  border-color: var(--clean-tint-strong);
  color: var(--clean-800);
}

.pill.rose {
  background: var(--rose-glass);
  border-color: var(--rose-tint-strong);
  color: var(--rose-700);
}

.pill.stone {
  background: var(--stone-glass);
  border-color: rgba(67, 81, 107, 0.3);
  color: var(--stone-700);
}

.pill.sky {
  background: var(--sky-glass);
  border-color: rgba(11, 92, 148, 0.3);
  color: var(--sky-800);
}

.pill-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  opacity: 0.85;
  text-align: center;
  margin-bottom: 5px;
}

.pill-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
}

.pill-btn {
  flex: none;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  cursor: pointer;
  color: inherit;
  transition: background var(--ease), transform var(--ease);
}

.pill-btn svg {
  width: 15px;
  height: 15px;
}

.pill-btn:hover {
  background: rgba(255, 255, 255, 0.55);
}

.pill-btn:active {
  background: rgba(255, 255, 255, 0.72);
  transform: scale(0.98);
}

/* Occupe la place restante au lieu d'une largeur fixe : c'est lui qui cède
   quand la colonne est étroite, jamais les boutons. */
.pill-val {
  flex: 1;
  min-width: 0;
  font-variant-numeric: tabular-nums;
  font-size: 18px;
  font-weight: 600;
  text-align: center;
}

/* Écran étroit (380px de référence, l'usage sur le terrain) : les trois
   compteurs doivent tenir côte à côte. À cette largeur il reste ~101px par
   colonne, soit 89px utiles — deux boutons de 28px, deux espaces de 4px et
   le compteur y entrent tout juste. */
@media (max-width: 420px) {
  .pills {
    gap: 6px;
  }

  .pill {
    padding: 8px 4px;
  }

  .pill-row {
    gap: 4px;
  }

  .pill-btn {
    width: 28px;
    height: 28px;
  }

  .pill-val {
    font-size: 17px;
  }
}

.transfers {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.transfer-btn {
  border: 1px solid var(--glass-border);
  background: rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-sm);
  padding: 10px 6px;
  font-size: 12px;
  min-height: 44px;
  color: var(--muted-2);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  transition: background var(--ease), transform var(--ease);
}

.transfer-btn:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.46);
}

.transfer-btn:active:not(:disabled) {
  background: rgba(255, 255, 255, 0.62);
  transform: scale(0.98);
}

.transfer-btn.full {
  grid-column: 1 / -1;
  background: var(--primary-glass);
  border-color: var(--primary-tint-strong);
  color: var(--primary-800);
  font-weight: 600;
}

/* Désactivé : aplat gris sans bordure et texte assourdi (~3:1, contre ~10:1
   à l'état actif). Le sélecteur reprend `.full` pour repasser devant la
   variante bleue pleine largeur, de même spécificité. */
.transfer-btn:disabled,
.transfer-btn.full:disabled {
  background: rgba(226, 232, 240, 0.6);
  border-color: transparent;
  color: #78879f;
  font-weight: 500;
  opacity: 1;
  cursor: not-allowed;
}

/* ---------- Badge ---------- */
/* Alerte : couleur franche, jamais un simple verre neutre. */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  padding: 4px 9px;
  border-radius: var(--radius-pill);
}

/* Icône légèrement plus grande que le texte du badge (14px, au lieu des
   12px hérités de 1em) pour rester bien lisible dans une petite pastille. */
.badge .icon-inline svg {
  width: 14px;
  height: 14px;
}

.badge-alert {
  color: var(--rose-700);
  background: var(--rose-glass);
}

/* ---------- Boutons ---------- */

/* Action principale de l'écran : couleur pleine et franche, texte blanc —
   jamais en verre. */
.btn-primary {
  width: 100%;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--primary-700);
  color: #fff;
  font-weight: 600;
  font-size: 15px;
  padding: 14px;
  min-height: 44px;
  cursor: pointer;
  box-shadow: 0 6px 16px var(--primary-shadow);
  transition: background var(--ease), transform var(--ease), box-shadow var(--ease);
}

.btn-primary:hover {
  background: var(--primary-hover);
}

.btn-primary:active {
  background: var(--primary-800);
  box-shadow: 0 2px 8px var(--primary-shadow-active);
  transform: scale(0.98);
}

/* Un simple abaissement d'opacité laissait le bleu dominer : le bouton
   paraissait actif. L'état désactivé perd donc sa couleur d'action et son
   ombre portée, et prend un curseur explicite. */
.btn-primary:disabled {
  background: #e2e8f0;
  color: #64748b;
  box-shadow: none;
  opacity: 1;
  cursor: not-allowed;
}

.btn-primary:disabled:hover {
  background: #e2e8f0;
}

.btn-ghost {
  width: 100%;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.3);
  color: var(--stone-700);
  font-weight: 600;
  font-size: 15px;
  padding: 14px;
  min-height: 44px;
  cursor: pointer;
  transition: background var(--ease), transform var(--ease);
}

.btn-ghost:hover {
  background: rgba(255, 255, 255, 0.46);
}

.btn-ghost:active {
  background: rgba(255, 255, 255, 0.62);
  transform: scale(0.98);
}

.btn-danger {
  background: var(--rose-600);
  color: #fff;
  box-shadow: 0 6px 16px rgba(181, 18, 47, 0.28);
}

.btn-danger:hover {
  background: #c31739;
}

.btn-danger:active {
  background: var(--rose-700);
  box-shadow: 0 2px 8px rgba(181, 18, 47, 0.3);
  transform: scale(0.98);
}

/* ---------- Modale ---------- */

.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  /* Voile volontairement léger : à 0.35 l'ardoise éteignait le dégradé
     pastel et donnait un arrière-plan gris sale. Aucun filtre de
     désaturation ici — seule l'opacité assombrit, la couleur reste. */
  background: rgba(30, 41, 59, 0.28);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

@media (min-width: 640px) {
  .modal-overlay {
    align-items: center;
    padding: 16px;
  }
}

/* Colonne : en-tête et pied fixes, seul le corps défile. Une modale de
   sélection du catalogue restait sinon coincée entre un titre parti vers le
   haut et un bouton d'action à retrouver tout en bas. */
.modal {
  width: 100%;
  max-width: 560px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding-bottom: var(--safe-bottom);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Contenu qui gagne à respirer : les catalogues de l'assistant, présentés
   en deux colonnes à partir de 640px (voir .catalog-group-items). */
.modal-wide {
  max-width: 640px;
}

@media (min-width: 640px) {
  .modal {
    border-radius: var(--radius-lg);
  }
}

.modal-head {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px 10px 20px;
  border-bottom: 1px solid var(--line);
}

/* `flex: 1` plutôt que de s'en remettre au space-between : dès qu'une flèche
   de retour s'ajoute à gauche (modale à écrans), c'est le titre qui doit
   occuper la place restante entre les deux boutons, et lui seul qui se
   tronque si le titre est long. */
.modal-head h2 {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La flèche de retour prend la place du padding gauche du titre. */
.modal-head.with-back {
  padding-left: 8px;
}

/* 36px plutôt que 44 : c'est ce bouton qui imposait la hauteur de l'en-tête,
   précieuse sur petit écran. Même taille que les autres boutons d'icône
   placés dans un en-tête de carte. */
.modal-head .close-btn,
.modal-head .modal-back {
  width: 36px;
  height: 36px;
}

.modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 18px 20px;
}

/* Pied fixe, hors de la zone défilante : les boutons d'action d'une longue
   liste restent atteignables sans dérouler jusqu'en bas. Fond un peu plus
   opaque que la modale et filet en haut — la bande se distingue du contenu
   qui défile derrière elle. La modale n'ayant pas de hauteur imposée (juste
   un plafond à 90vh), ce pied ne décolle pas du contenu tant que la modale
   est courte : c'est le corps qui se rétracte une fois le plafond atteint. */
.modal-foot {
  flex-shrink: 0;
  padding: 14px 20px 16px;
  border-top: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.72);
}

.field {
  margin-bottom: 14px;
}

.field label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-2);
  margin-bottom: 6px;
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
}

.modal-text {
  font-size: 14px;
  color: var(--muted-2);
  margin: 0 0 16px;
}

.modal-btns {
  display: flex;
  gap: 8px;
}

.sync-note {
  text-align: center;
  font-size: 12px;
  color: var(--muted);
  padding-top: 4px;
}

/* ---------- Collaborateurs (écran de "Mon compte") ---------- */

.share-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.share-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(255, 255, 255, 0.28);
  border-radius: var(--radius-sm);
  padding: 8px 8px 8px 14px;
  font-size: 14px;
}

/* Se déconnecter n'est pas une action destructrice : bouton secondaire
   neutre. Le rouge (.btn-danger) reste réservé à la suppression de compte
   et d'articles. */
.logout-action {
  margin-top: 8px;
}

/* ---------- Mon compte : menu et écrans de section ---------- */
/* Une seule modale, dont le corps change d'écran (voir accountModal dans
   app.js). L'animation ne porte que sur l'écran entrant : la modale, elle,
   ne bouge pas — sa hauteur suit simplement son contenu. */

@keyframes account-screen-in {
  from {
    opacity: 0;
    transform: translateX(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes account-screen-back {
  from {
    opacity: 0;
    transform: translateX(-14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.account-screen-in {
  animation: account-screen-in 180ms ease;
}

.account-screen-back {
  animation: account-screen-back 180ms ease;
}

/* Respect d'un système réglé sur "moins d'animations" : le glissement
   latéral disparaît, l'écran change sans transition. */
@media (prefers-reduced-motion: reduce) {
  .account-screen-in,
  .account-screen-back {
    animation: none;
  }
}

/* Bloc d'identité en tête du menu : le nom domine, l'email l'identifie, les
   mentions (conciergerie, plan, ancienneté) restent en retrait. */
.account-identity {
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
}

.account-identity p {
  margin: 0;
  overflow-wrap: anywhere;
}

.account-identity-name {
  font-size: 17px;
  font-weight: 700;
  color: var(--ink);
}

.account-identity-email {
  font-size: 14px;
  color: var(--muted-2);
}

.account-identity-meta {
  margin-top: 4px !important;
  font-size: 12px;
  color: var(--muted);
}

.account-flash {
  margin: 0 0 12px;
}

.account-menu {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Ligne pleine largeur : même facture que les lignes-cartes des panneaux du
   tableau de bord (.summary-row). Le confort tactile vient du contenu — deux
   lignes de texte à côté d'une pastille de 36px dépassent largement les
   44px requis — mais min-height le garantit quoi qu'il arrive. */
.account-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 44px;
  border: none;
  border-radius: 12px;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.28);
  text-align: left;
  cursor: pointer;
  transition: background var(--ease), transform var(--ease);
}

.account-row:hover {
  background: rgba(255, 255, 255, 0.6);
}

.account-row:active {
  transform: scale(0.99);
}

.account-row-badge {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--primary-glass);
  color: var(--primary-800);
}

.account-row-badge svg {
  width: 18px;
  height: 18px;
}

.account-row-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.account-row-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

.account-row-desc {
  font-size: 12px;
  line-height: 1.35;
  color: var(--muted);
}

.account-row-chevron {
  flex-shrink: 0;
  font-size: 18px;
  line-height: 1;
  color: var(--muted);
}

/* Suppression du compte : détachée de la liste par son écart et son filet,
   et seule entrée à porter le rouge. */
.account-menu-danger {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.account-row-danger .account-row-label {
  color: var(--rose-700);
}

.account-row-danger .account-row-badge {
  background: var(--rose-glass);
  color: var(--rose-700);
}

/* Un seul bouton d'action par écran de section, précédé de ses messages. */
.account-actions {
  margin-top: 4px;
}

.account-actions .login-error,
.account-actions .login-notice {
  margin: 0 0 10px;
}

/* Le champ d'invitation suit la liste des collaborateurs : un écart franc
   sépare ce qui existe de ce qu'on ajoute. */
.share-invite-field {
  margin-top: 18px;
}

.modal-section {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.modal-section h3 {
  margin: 0 0 12px;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-2);
}

.modal-section .btn-primary,
.modal-section .btn-ghost {
  margin-top: 4px;
}

/* ---------- Assistant de création de logement (catalogue) ---------- */

.catalog-group {
  margin-bottom: 18px;
}

.catalog-group-title {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-2);
}

/* Deux colonnes dès que la modale a la largeur pour (elle passe à 640px,
   voir .modal-wide) : un catalogue d'une quinzaine d'articles tient alors
   en deux fois moins de défilement. Une seule colonne en dessous — sur
   téléphone, deux cases à cocher côte à côte seraient trop serrées.
   Étirement (valeur par défaut, surtout pas `align-items: start`) : les
   deux cartes d'une même ligne gardent la même hauteur quand l'une est
   cochée et se déplie sur ses champs de quantité. Avec `start` elles
   restaient calées en haut, de hauteurs inégales, et la ligne se trouait. */
.catalog-group-items {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}

@media (min-width: 640px) {
  .catalog-group-items {
    grid-template-columns: 1fr 1fr;
  }
}

/* Répété potentiellement une quinzaine de fois dans l'assistant : verre
   translucide sans flou. */
.catalog-item {
  background: rgba(255, 255, 255, 0.28);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}

/* Coché : la carte elle-même le confirme, pas seulement la case — dans une
   grille à deux colonnes, la coche seule se repère mal. */
.catalog-item.checked {
  background: var(--primary-glass);
  border-color: var(--primary-tint-strong);
}

.catalog-check {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  font-size: 15px;
  min-height: 44px;
}

.catalog-check input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.42);
  border: 1px solid rgba(255, 255, 255, 0.6);
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}

.catalog-check input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--primary-tint);
}

.catalog-check input[type="checkbox"]:checked {
  background-color: var(--primary-700);
  border-color: var(--primary-700);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 13 10 18 19 7'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 16px 16px;
}

/* Champs de quantité : même mécanique de dépliage que partout ailleurs dans
   l'appli (grid-template-rows 0fr → 1fr), qui s'anime vers la hauteur réelle
   du contenu sans max-height arbitraire. Remplace un display:none commuté,
   dont l'apparition brutale décalait toute la grille d'un coup.

   Trois niveaux, et c'est nécessaire : l'élément qui se replie à zéro ne
   peut porter ni marge, ni remplissage, ni bordure — ceux-là ne sont pas
   rognés par la hauteur nulle et laisseraient un liseré sous chaque carte
   non cochée. D'où .catalog-qty-clip, nu, entre le conteneur animé et les
   champs qui portent la séparation. */
.catalog-qty-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 200ms ease;
}

.catalog-qty-wrap.open {
  grid-template-rows: 1fr;
}

.catalog-qty-clip {
  min-height: 0;
  overflow: hidden;
}

.catalog-qty {
  display: flex;
  gap: 10px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.catalog-qty-field {
  flex: 1;
  min-width: 0;
}

.catalog-qty-field label {
  display: block;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-2);
  margin-bottom: 4px;
}

.catalog-qty-field input {
  padding: 9px 10px;
  border-radius: 10px;
}

/* La nature n'est plus demandée ici (l'étape de l'assistant la décide) : la
   rangée n'a plus que nom, quantité, seuil et le bouton de retrait. Le nom
   prend sa propre ligne sur téléphone, où le comprimer à côté de deux
   champs numériques ne laissait plus lire ce qu'on tape. */
.custom-item-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-bottom: 10px;
}

.custom-item-row .custom-name {
  flex: 1 1 100%;
  min-width: 0;
}

.custom-item-row .custom-clean,
.custom-item-row .custom-threshold {
  flex: 1;
  min-width: 0;
}

@media (min-width: 640px) {
  .custom-item-row .custom-name {
    flex: 2 1 0;
  }
}

.custom-item-row input {
  padding: 9px 10px;
  font-size: 14px;
  border-radius: 10px;
}

.custom-item-row .icon-btn {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  font-size: 16px;
}
