/* ============ NEKO SHARED TOKENS & PRIMITIVES ============ */
/* Lora est self-hostée (/fonts/lora.css) pour éviter le traceur Google Fonts (RGPD).
   Les autres polices (Barlow, etc.) servaient uniquement sur la landing
   React via les variables --serif/--display/--mono. En attendant leur self-host,
   elles tombent sur les fallbacks Georgia/sans-serif définis plus bas. */
@import url('/fonts/lora.css');

:root{
  /* Palette alignee sur la landing page (restyle.css) */
  --cream:#FBF7F3;
  --cream-2:#F5EBDF;
  --cream-3:#ECD9C5;
  --terracotta:#FF6B5A;
  --terracotta-deep:#E04A39;
  --sage:#999999;
  --sage-deep:#777777;
  --ink:#1F1A1A;
  --ink-soft:#6B5A52;
  --muted:#8A7A70;
  --line:#EADDD0;
  /* Filet corail « façon app » — transpose CARD_BORDER (mobile/src/theme/cards.ts) :
     bordure corail 1.5px + fond rosé subtil. À utiliser sur les cartes de l'espace
     owner pour que le web colle au rendu de l'app mobile (pas de liseré gris). */
  --card-filet:rgba(255,107,90,.7);
  --card-rose:#FBF1EC;
  --white:#FFFFFF;
  --danger:#B84A3A;
  --success:#777777;
  --shadow: 0 1px 0 rgba(26,22,20,0.04), 0 10px 30px -12px rgba(26,22,20,0.12);
  --shadow-lift: 0 2px 0 rgba(26,22,20,0.04), 0 24px 50px -18px rgba(26,22,20,0.22);
  --radius: 10px;
  --radius-lg: 14px;
  --serif: "Lora", Georgia, serif;
  --sans: "Instrument Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
  --display: "Lora", Georgia, serif;
  /* Hauteur de la ZONE DES ONGLETS du dock du bas (mobile), hors inset système.
     Miroir de TAB_BAR_HEIGHT (mobile/src/components/TabBar.tsx).
     ⚠️ Source unique : le padding bas du body ET toutes les barres d'action
     fixées des pages (.jg-bar, .cn-bar, .sign-bottom) s'en servent. Ne jamais
     recopier la valeur en dur ailleurs, sinon le décalage se désynchronise
     comme l'a fait le « 68px » de /classified-new après le retrait de la
     première barre du bas (juillet 2026). */
  --neko-dock-h: 72px;
  /* Écart entre la barre de nav flottante et les bords de la fenêtre.
     ⚠️ Source unique : `.nav` s'en sert pour sa marge ET son `top` collant, et
     le drawer du burger s'en sert pour ANNULER cet écart (voir plus bas). Ces
     deux usages doivent bouger ensemble : le drawer est un descendant fixe de
     `.nav`, donc ses coordonnées se résolvent contre la nav, pas contre la
     fenêtre. Une valeur recopiée en dur d'un côté décalerait le drawer de
     l'autre. */
  --nav-inset: 16px;
}

/* role accents */
body[data-role="owner"]{ --role:#ff6262; --role-deep:#CC3A20; --role-soft:#FFE8E0 }
body[data-role="sitter"]{ --role:#999999; --role-deep:#777777; --role-soft:#E0E0E0 }

*{box-sizing:border-box}
html,body{margin:0;padding:0}
html{scroll-behavior:smooth}
body{
  font-family: var(--sans);
  background: var(--cream);
  color: var(--ink);
  font-size: 16px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
}
/* Fond crème uni — pas de grain/pointillés */
img{max-width:100%;display:block}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
a{color:inherit;text-decoration:none}
input,select,textarea{font:inherit;color:inherit}

/* ---------- Typography (alignee sur la landing : Lora partout) ---------- */
.serif{font-family:var(--serif); font-weight:400; letter-spacing:-0.01em}
.mono{font-family:var(--mono); font-size:11px; letter-spacing:0.04em; text-transform:uppercase; color:var(--muted)}
/* Eyebrow style landing : Lora italique au lieu de mono uppercase */
.eyebrow{
  font-family:var(--serif); font-style:italic; font-weight:400;
  font-size:18px; letter-spacing:0; text-transform:none;
  color: var(--terracotta); display:inline-flex; align-items:center; gap:10px;
}
.eyebrow::before{ content:""; width:18px; height:1px; background:var(--terracotta); }
/* Titres en Lora comme la landing (override section h2 → Lora dans globals.css) */
h1,h2,h3,h4{font-family:"Lora", Georgia, serif; font-weight:600; letter-spacing:-0.01em; margin:0; font-style:normal}
/* Aucun titre en italique (les mots corail <em> des titres restent droits) */
h1 em,h2 em,h3 em,h4 em,h1 i,h2 i,h3 i,h4 i{font-style:normal}
h1{font-size: clamp(32px, 4.6vw, 52px); line-height:1.05}
h1 em{color:var(--terracotta); font-weight:400}
h2{font-size: clamp(24px, 2.6vw, 34px); line-height:1.15}
h2 em{color:var(--terracotta); font-weight:400}
h3{font-size: 20px; line-height:1.25; font-weight:500}
h4{font-size: 17px; line-height:1.3; font-weight:500}
.lede{font-size:17px; color:var(--ink-soft); line-height:1.55}

/* ---------- Layout ---------- */
.wrap{max-width:1240px; margin:0 auto; padding: 0 28px; position:relative; z-index:1}
.wrap-narrow{max-width:640px; margin:0 auto; padding: 0 28px; position:relative; z-index:1}
/* ⚠️ UN CADRE `?embed=` QUI OUVRE UNE MODALE PASSE EN PLEIN ÉCRAN (2026-08-31).
   `nekoEmbedBootstrap` (shared.js) pose cette classe sur le <body> de l'HÔTE
   quand la page embarquée ouvre une fenêtre : sans quoi le voile s'arrête pile
   où l'iframe s'arrête et la boîte de dialogue se centre sur la hauteur du
   cadre, souvent sous le pli. Le motif, l'historique et les trois pièges sont
   au-dessus de ce code-là — ne pas les recopier ici.
   ⚠️ `overflow` gèle l'hôte : lui seul défile encore, l'iframe étant fixée.
   ⚠️ LA REMONTÉE DE `.wrap` N'EST PAS DÉCORATIVE. `.wrap` porte `z-index:1`,
   donc un contexte d'empilement qui EMPRISONNE celui de l'iframe : son
   `z-index:2147483000` ne vaut qu'à l'intérieur, et le dock (80), le hamburger
   (90) ou la barre de nav (100–130), montés sur <body>, passeraient devant un
   cadre censé couvrir l'écran. Même piège et même correctif que
   `body.sd-journal-modal #sdash` dans /espace-sitter.
   ⚠️ La classe part avec la fermeture de la modale, et un `pagehide` la retire
   si le cadre disparaît pendant : sans ce filet, l'hôte resterait figé. */
body.neko-embed-modal{overflow:hidden}
body.neko-embed-modal .wrap, body.neko-embed-modal .wrap-narrow{z-index:4200}
section{position:relative; z-index:1}

/* ---------- NAV (volante glass comme la landing) ---------- */
.nav{
  position:sticky; top:var(--nav-inset); z-index:50;
  margin: var(--nav-inset) var(--nav-inset) 0;
  border-radius: 999px;
  backdrop-filter: blur(12px) saturate(1.2);
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
  background: rgba(251, 246, 240, 0.88);
  border-bottom: none;
  box-shadow: 0 6px 20px -8px rgba(60, 40, 30, 0.18);
  transition: box-shadow .3s ease;
}
.nav.scrolled{box-shadow: 0 8px 24px -10px rgba(60, 40, 30, 0.25)}
.nav-inner{display:flex; align-items:center; justify-content:space-between; padding: 8px 20px}
.nav-back{
  flex:0 0 auto; width:32px; height:36px; margin-right:6px;
  display:grid; place-items:center; color:var(--terracotta);
  border:0; background:transparent; padding:0;
  transition: color .2s, transform .15s;
}
.nav-back:hover{color:var(--terracotta); transform:translateX(-2px)}
.nav-inner .logo{margin-right:auto}
.logo{display:flex; align-items:center; gap:10px; font-family:"Lora", Georgia, serif; font-size:26px; font-weight:700; letter-spacing:0.04em; text-transform:uppercase; color:var(--ink)}
.logo-mark{
  width:34px; height:34px; display:grid; place-items:center; position:relative;
  transition:transform .18s ease, filter .22s ease;
}
.logo-mark svg{ width:100%; height:100%; }
.logo-mark img{ width:100%; height:100%; object-fit:contain; display:block; }
a.logo:hover .logo-mark{ transform:translateY(-2px); filter:drop-shadow(0 6px 10px rgba(0,0,0,.16)); }
a.logo:active .logo-mark{ transform:translateY(0) scale(.94); filter:drop-shadow(0 2px 4px rgba(0,0,0,.14)); }
/* Logo de la barre de nav agrandi (même taille que la landing) et sans le mot « Neko ».
   Scopé à .nav → le logo du footer n'est pas touché. S'applique à toutes les pages. */
.nav .logo{ font-size:0; gap:0; }
.nav .logo-mark{ width:90px; height:90px; margin-top:-28px; margin-bottom:-28px; }
@media (max-width: 820px){
  .nav .logo-mark{ width:70px; height:70px; margin-top:-22px; margin-bottom:-22px; }
}
.nav-links{display:flex; gap:24px; align-items:center}
.nav-links a{font-size:14px; color:var(--ink); font-weight:500; transition:color .2s}
.nav-links a:hover, .nav-links a.active{color:var(--terracotta)}
.nav-links a.active{font-weight:600}
.nav-links a.nav-cta{padding:10px 20px; border-radius:6px; background:var(--terracotta); color:#fff; font-size:14px; font-weight:600; transition:background .2s}
.nav-links a.nav-cta:hover{background:var(--terracotta-deep); color:#fff}

/* Profile dropdown */
.profile-menu{position:relative}
.profile-btn{
  display:flex; align-items:center; gap:8px;
  padding:6px 10px 6px 6px; border-radius:999px;
  border:1px solid var(--line); background:var(--white);
  transition: border-color .2s;
}
.profile-btn:hover{border-color:var(--ink)}
.profile-avatar{
  width:28px; height:28px; border-radius:50%;
  display:grid; place-items:center;
  font-family:var(--serif); font-style:italic; font-size:13px;
  color:var(--white); background: var(--terracotta);
}
body[data-role="sitter"] .profile-avatar{background: var(--sage-deep)}
.profile-btn span{font-size:13px; font-weight:500}
.profile-btn .caret{font-size:9px; color:var(--muted)}
.profile-dropdown{
  position:absolute; top:calc(100% + 8px); right:0;
  background:var(--white); border:1px solid var(--line);
  border-radius: 12px; box-shadow: var(--shadow-lift);
  min-width:240px; padding: 8px; display:none; z-index:60;
}
.profile-dropdown.open{display:block; animation: fadeIn .15s ease}
.profile-dropdown .pd-head{padding: 10px 12px 12px; border-bottom:1px solid var(--line); margin-bottom:6px}
.profile-dropdown .pd-head b{display:block; font-size:14px}
.profile-dropdown .pd-head small{color:var(--muted); font-family:var(--mono); font-size:10px; letter-spacing:0.08em; text-transform:uppercase}
.profile-dropdown a{display:flex; align-items:center; gap:10px; padding: 9px 12px; border-radius:8px; font-size:14px; color:var(--ink-soft); transition:background .15s}
.profile-dropdown a:hover{background:var(--cream-2); color:var(--ink)}
.profile-dropdown hr{border:0; border-top:1px solid var(--line); margin:6px 0}
@keyframes fadeIn{from{opacity:0; transform:translateY(-4px)} to{opacity:1; transform:translateY(0)}}

.nav-cta .nav-cta-txt{margin-left:5px}

/* ⚠️ Burger du menu public : 3 barres qui se croisent en X à l'ouverture,
   comme la landing (src/components/Navbar.tsx). Ce n'est PAS la pastille ronde
   .nav-bell des dashboards : le menu public doit être identique partout. */
.nav-burger-wrap{position:relative; display:flex}
.nav-burger{
  display:inline-flex; flex-direction:column; justify-content:center; align-items:center;
  gap:5px; width:40px; height:40px; padding:0; margin-left:8px;
  background:transparent; border:0; cursor:pointer; border-radius:10px;
  transition:background .2s;
}
.nav-burger:hover, .nav-burger.open{border:0; background:color-mix(in oklab, var(--ink) 8%, transparent)}
.nav-burger span{
  display:block; width:22px; height:2px; background:var(--ink); border-radius:2px;
  transition:transform .25s, opacity .2s;
}
.nav-burger.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.nav-burger.open span:nth-child(2){opacity:0}
.nav-burger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.nav-burger-menu{min-width:280px}
/* les liens du menu burger sont dans .nav-links : ne pas les masquer en mobile */
/* ⚠️ `gap:0` OBLIGATOIRE. Ces liens héritent de `.profile-dropdown a`, qui pose
   un `gap:10px` destiné à séparer une icône de son libellé. Or ici les enfants
   ne sont pas « icône + texte » mais UNE LETTRE PAR SPAN (nekoNavLetters, pour
   l'effet de survol) : le gap s'insérait donc entre chaque lettre et le menu
   s'affichait « S e   c o n n e c t e r ». */
.nav-burger-menu a{display:flex!important; align-items:center; gap:0}

/* Survol « slide-replace » des liens de menu, repris de la landing : chaque
   lettre porte 2 couches, la sans-serif monte pendant que la Lora italique
   remonte du bas. Le markup vient de nekoNavLetters() (shared.js). */
.nav-letter{
  position:relative; display:inline-block; flex:none; overflow:hidden;
  vertical-align:top; line-height:1.2; white-space:pre;
}
.nav-letter-base, .nav-letter-alt{
  display:inline-block; will-change:transform;
  transition:transform .45s cubic-bezier(.6,0,.2,1);
  transition-delay:calc(var(--i, 0) * .025s);
}
.nav-letter-alt{
  position:absolute; top:0; left:0; transform:translateY(110%);
  font-family:var(--serif); font-style:italic; font-weight:200; white-space:pre;
}
.nav-links a:hover .nav-letter-base,
.nav-burger-menu a:hover .nav-letter-base{transform:translateY(-110%)}
.nav-links a:hover .nav-letter-alt,
.nav-burger-menu a:hover .nav-letter-alt{transform:translateY(0)}
@media (prefers-reduced-motion: reduce){
  .nav-letter-base, .nav-letter-alt{transition:none}
}
/* Bouton fermer (X) du drawer : masqué en desktop (simple dropdown) */
.nav-burger-menu .nav-drawer-close{display:none}

/* Menu burger → drawer plein hauteur qui glisse depuis la droite (façon Claude),
   réutilise le voile .neko-drawer-backdrop. Actif sous 820px, comme la bascule
   des liens de la nav.

   ⚠️ CE DRAWER EST BIEN FIXÉ À LA FENÊTRE DEPUIS LE 2026-08-19, parce que
   nekoToggleBurger() le PORTE DANS LE <body> le temps de son ouverture. Ne pas
   « simplifier » en le laissant dans la nav : `.nav` porte un `backdrop-filter`,
   donc elle devient le bloc conteneur de ses descendants `fixed`, et `top/right`
   se résolvaient contre ELLE, décalée de --nav-inset des quatre côtés.
   ⚠️ Les offsets NÉGATIFS qui compensaient cet écart (`calc(-1 * var(--nav-inset))`)
   sont donc retirés : les remettre décalerait le tiroir de 16px hors de l'écran.
   Le montage tenait tant que le flou s'appliquait — c'est ce qui a cassé le menu
   de la landing, corrigé le 2026-08-18 de la même façon (cf. Navbar.tsx).
   ⚠️ La nav n'a plus besoin de repasser au-dessus du voile : le panneau et le
   voile sont désormais frères dans le <body> (100 contre 90), et le voile ne
   peut plus se peindre par-dessus le menu. La règle `body.nav-drawer-open .nav`
   qui vivait ici a été SUPPRIMÉE : la rétablir remettrait la nav (120) devant le
   tiroir (100), soit exactement le défaut inverse.

   ⚠️ MAIS L'ÉTAT FERMÉ RESTE `display:none`, et ce n'est pas décoratif : hors
   ouverture le panneau retourne DANS la nav, où il retrouve son faux `fixed`.
   Un descendant fixe dont le bloc conteneur n'est pas la fenêtre AGRANDIT LA
   ZONE DE DÉFILEMENT du document : posé à `translateX(100%)`, il ajoutait ~360px
   à droite de CHAQUE page statique, la page défilait latéralement et laissait
   voir le menu hors-champ (incident 2026-07-29, remonté depuis /connexion sur
   Android). C'est aussi pour ça que la classe `mounted` existe : il faut que
   l'élément soit affiché À translateX(100%) une frame avant de lancer la
   transition, sinon il apparaît d'un coup. Voir nekoToggleBurger() dans
   shared.js : les deux moitiés du mécanisme doivent rester d'accord. */
@media (max-width: 820px){
  .nav-burger-menu.profile-dropdown{
    position:fixed; top:0; right:0;
    left:auto; bottom:auto;
    height:100vh; height:100dvh; width:min(360px, 86vw); min-width:0;
    border:0; border-left:1px solid var(--line); border-radius:0;
    box-shadow:-24px 0 60px -24px rgba(26,22,20,.35);
    padding:84px 12px 28px;
    display:none; transform:translateX(100%);
    transition:transform .32s cubic-bezier(.4,0,.2,1);
    z-index:100; overflow-y:auto; overscroll-behavior:contain; animation:none;
  }
  /* Affiché mais encore hors-champ : l'état de départ de la glissade. */
  .nav-burger-menu.profile-dropdown.mounted{display:block}
  .nav-burger-menu.profile-dropdown.open{display:block; transform:translateX(0); animation:none}
  .nav-burger-menu.profile-dropdown a{padding:14px 16px; font-size:16px}
  .nav-burger-menu .nav-drawer-close{
    display:grid; place-items:center; position:absolute; top:16px; right:16px;
    width:38px; height:38px; border-radius:50%;
    background:var(--white); border:1px solid var(--line); color:var(--ink); cursor:pointer;
    transition:border-color .2s, color .2s;
  }
  .nav-burger-menu .nav-drawer-close:hover{border-color:var(--ink); color:var(--terracotta)}
}

@media (max-width: 820px){
  .nav-links a:not(.nav-cta):not(.profile-btn-link){display:none}
  .nav-inner.wrap{padding-left:14px; padding-right:14px}
  .nav-links{gap:10px}
  .logo{font-size:21px; gap:7px}
  .logo-mark{width:28px; height:28px}
  /* CTA « + Créer une annonce » → bouton rond compact « + » */
  .nav-links a.nav-cta{padding:0; width:40px; height:40px; display:grid; place-items:center; border-radius:50%; font-size:24px; line-height:1}
  .nav-cta .nav-cta-txt{display:none}
  .nav-cta .nav-cta-ico{margin-top:-2px}
  .nav-bell{width:38px; height:38px}
  .nav-bell svg{width:18px; height:18px}
  /* profil : avatar seul, on masque le prénom + le chevron */
  .profile-btn{padding:5px; gap:0}
  .profile-btn > span:not(.profile-avatar){display:none}
}

/* Cloche notifications (haut droite) */
.nav-bell{
  position:relative; width:40px; height:40px; border-radius:50%;
  display:grid; place-items:center; color:var(--ink);
  border:1px solid var(--line); background:var(--white);
  transition: border-color .2s, color .2s, transform .15s;
}
.nav-bell:hover{border-color:var(--ink); color:var(--terracotta); transform:translateY(-1px)}
.nav-bell-dot{
  position:absolute; top:8px; right:9px; width:8px; height:8px; border-radius:50%;
  background:var(--terracotta); border:2px solid var(--white);
}

/* ---------- DRAWER notifications (panneau coulissant à droite, façon panneau document) ---------- */
/* ⚠️ Empilement : 125/130, AU-DESSUS de la capsule flottante « Messagerie /
   Notifications » (.neko-fab-dock, z-index 120, desktop). À 90/100 les deux
   pilules blanches se peignaient PAR-DESSUS l'en-tête du tiroir ouvert : on
   voyait « Messagerie » et « Notifications » flotter sur le fil de discussion.
   Un tiroir ouvert doit passer devant tout, y compris ce qui l'a ouvert.
   Reste SOUS le tiroir de navigation (.neko-tabbar, 200), comme avant. */
.neko-drawer-backdrop{
  position:fixed; inset:0; z-index:125;
  background: rgba(26,22,20,0.32); backdrop-filter: blur(2px);
  opacity:0; visibility:hidden; transition: opacity .25s ease, visibility .25s ease;
}
/* ⚠️ EXCEPTION — le voile du menu burger réutilise ce style mais PAS ce niveau.
   Depuis le portage du 2026-08-19, son menu est un FRÈRE de ce voile dans le
   <body>, à z-index 100 : un voile à 125 repasserait donc devant lui, qui
   ressortirait grisé et flouté (blur(2px)) au lieu d'être net. Ne pas
   « corriger » en retirant le flou du voile : il est voulu sur la page, c'est
   seulement le menu qui ne doit pas le subir. */
#nav-burger-backdrop{ z-index:90; }
.neko-drawer-backdrop.open{opacity:1; visibility:visible}
.neko-drawer{
  position:fixed; top:0; right:0; z-index:130;
  width: min(420px, 92vw); height:100vh; height:100dvh;
  background:var(--cream); border-left:1px solid var(--line);
  box-shadow: -24px 0 60px -24px rgba(26,22,20,0.35);
  display:flex; flex-direction:column;
  transform: translateX(100%); transition: transform .32s cubic-bezier(.4,0,.2,1);
}
.neko-drawer.open{transform: translateX(0)}
.neko-drawer-head{
  display:flex; align-items:center; justify-content:space-between;
  padding: 22px 22px 16px; border-bottom:1px solid var(--line); flex-shrink:0;
}
.neko-drawer-close{
  width:34px; height:34px; border-radius:50%; display:grid; place-items:center;
  color:var(--ink-soft); border:1px solid var(--line); background:var(--white);
  transition: border-color .2s, color .2s;
}
.neko-drawer-close:hover{border-color:var(--ink); color:var(--ink)}
/* overscroll-behavior:contain — sans lui, arrivé en bout de liste le scroll
   « traverse » le drawer et fait défiler la page derrière (règle transversale :
   un popup ouvert scrolle seul, la page ne bouge pas). */
.neko-drawer-body{flex:1; overflow-y:auto; overscroll-behavior:contain; padding: 16px 18px 32px}
.neko-drawer-empty{text-align:center; padding: 48px 16px; color:var(--muted)}
.neko-drawer-empty b{display:block; font-family:"Lora", Georgia, serif; font-weight:600; font-size:17px; color:var(--ink)}
.neko-drawer-empty p{font-size:13px; margin-top:6px}
/* item de notification */
.nt{display:flex; align-items:flex-start; gap:12px; padding:14px; border-radius:var(--radius-lg); background:var(--white); border:1px solid var(--line); box-shadow:var(--shadow); margin-bottom:10px; transition:transform .15s, box-shadow .25s; cursor:pointer}
.nt:hover{transform:translateY(-2px); box-shadow:var(--shadow-lift)}

/* ---------- MESSAGERIE drawer (panneau coulissant à droite) ---------- */
.neko-msg-drawer{width: min(480px, 96vw)}
.neko-msg-drawer .neko-drawer-head{gap:12px}
/* ⚠️ LE TITRE « Messages » EST VISIBLE PARTOUT depuis le 2026-08-15. Il était
   masqué par défaut et rallumé dans deux contextes seulement (≤767px et
   `body.neko-msg-pinned`), parce que le tiroir coulissant portait à sa place un
   sur-titre « Messagerie ». Ce sur-titre a été SUPPRIMÉ DU DOM le même jour
   (demande Julie, cf. `nekoRenderMsgList`) : le laisser masqué ici donnerait au
   tiroir un en-tête ne contenant plus que sa croix de fermeture, entre 768 et
   1024px et sur toute page qui n'épingle pas la colonne. C'est le mode que
   personne ne regarde et où les régressions s'installent.
   ⚠️ La pillule « Berlioz · IA » et la recherche, elles, RESTENT masquées par
   défaut, et pour deux raisons différentes : la pillule ferait doublon avec la
   grande carte « Parlez à Berlioz » du pied, que le tiroir coulissant est le
   seul à garder (mobile et colonne épinglée la masquent, d'où la pillule à la
   place) ; la recherche est mobile seule. Elles restent dans le DOM, c'est ici
   qu'on les masque. */
.neko-msg-botpill,
.neko-msg-search{display:none}
/* ⚠️ DESSIN DU TITRE ET DE LA PILLULE — hors de toute media query. Ils ont vécu
   dans le bloc ≤767px jusqu'au 2026-08-15 ; la colonne épinglée les demandant à
   son tour, les recopier en aurait fait deux exemplaires qui divergent au
   premier ajustement — et l'un d'eux porte un décalage d'image calculé à la
   main, exactement le genre de valeur qu'on ne veut pas voir en double.
   ⚠️ Le `display:block` du TITRE est ici, et nulle part ailleurs : il est visible
   dans les trois modes depuis le retrait du sur-titre (cf. le bloc précédent).
   La PILLULE, elle, garde son `display` dans chaque mode qui la rallume — c'est
   elle qui varie, pas le titre. */
/* ⚠️ **SANS GRAS** (demande Julie, 2026-08-30 : « enlève le gras de Messages »).
   La balise est un `<b>` — c'est le CSS qui décide, pas le nom de l'élément : sans
   `font-weight` explicite le navigateur retomberait sur le 700 par défaut du `b`,
   soit plus gras qu'avant. Ne pas retirer la déclaration en croyant l'annuler. */
.neko-msg-listtitle b{
  display: block;
  font-family: "Lora", Georgia, serif; font-weight: 400;
  font-size: 26px; letter-spacing: -.5px; line-height: 1.1; color: var(--ink);
}
/* ⚠️ MÊME DESSIN QUE `.neko-side-cta` depuis le 2026-08-25 (demande Julie :
   « je veux un style de CTA comme les autres de mon style »). La pillule avait
   sa propre recette — 999px, liseré encre de 1,5px en `border`, tranche 3D de
   3px, Lora 600 — c'est-à-dire trois écarts avec le seul bouton corail du site.
   Recette commune, réécrite ici et pas héritée (aucune classe partagée entre le
   tiroir de messagerie et la colonne de gauche) : fond `--terracotta`, texte
   blanc, liseré encre DESSINÉ EN ::before et remonté de 3px, tranche 3D #C44C3D
   d'1px dessous, Lora 400. ⚠️ Si `.neko-side-cta` change, ceci change à la main.
   ⚠️ Le liseré est en ::before et NON en `border` : une bordure suivrait le
   bouton dans son enfoncement au clic, alors que le trait doit rester posé.
   D'où le `position:relative`, sans lequel le ::before se calerait sur l'en-tête.
   ⚠️ Le rembourrage gauche reste plus court que le droit : l'illustration
   déborde de son cadre de 26×24 (cf. `.neko-msg-botpill-ic`). */
.neko-msg-botpill{
  position: relative;
  align-items: center; gap: 8px; flex: 0 0 auto;
  padding: 11px 16px 11px 11px; border-radius: 12px;
  border: 1px solid transparent; background: var(--terracotta); color: #fff;
  text-decoration: none; box-shadow: 0 1px 0 0 #C44C3D;
  font-family: "Lora", Georgia, serif; font-weight: 400; font-size: 14px; line-height: 1.2;
  transition: transform .1s ease, box-shadow .1s ease;
  will-change: transform;
}
.neko-msg-botpill::before{
  content: ""; position: absolute; inset: 0;
  border: 1px solid #1F1A1A; border-radius: inherit;
  transform: translateY(-3px); pointer-events: none;
}
.neko-msg-botpill:hover{ transform: translateY(-1px); box-shadow: 0 2px 0 0 #C44C3D; }
.neko-msg-botpill:active{ transform: translateY(1px); box-shadow: 0 0 0 0 #C44C3D; }
@media (prefers-reduced-motion: reduce){
  .neko-msg-botpill{ transition: none; }
  .neko-msg-botpill:hover, .neko-msg-botpill:active{ transform: none; }
}
/* Gabarit fixe : l'illustration déborde de son cadre SANS agrandir la pillule
   (même ruse que le bouton de l'appli, où l'image fait 44px dans 26×24). */
.neko-msg-botpill-ic{ position: relative; width: 26px; height: 24px; flex: 0 0 auto; }
/* ⚠️ Centrer l'IMAGE ne centre pas le DESSIN : il n'est pas centré dans son
   propre fichier. Mesure de la boîte alpha du dessin d'origine, désormais rangé
   hors de public/ : _illustrations_originaux/messagerie.png (canvas 1536×1024) :
   dessin sur x 432..1139 et y 202..769, donc un centre à +17,5px / −26,5px de
   celui du canvas. Rendu à 44px de large (échelle 0,0286), cela fait 0,5px trop
   à droite et 0,76px trop haut : indécelable en 1x, ~2 pixels physiques sur un
   écran de téléphone, d'où l'impression de décalage dans la pillule. Le
   translate corrige exactement ce biais.
   ⚠️ La pillule sert désormais messagerie.webp (384×256), qui est le MÊME dessin
   réduit proportionnellement, sans recadrage : le biais est un pourcentage du
   canvas, il traverse donc la réduction inchangé et les deux valeurs ci-dessous
   restent justes.
   ⚠️ Valeurs liées à CE dessin : les recalculer s'il est réexporté (un
   recadrage, lui, les invaliderait). */
.neko-msg-botpill-ic img{
  position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; max-width: none; object-fit: contain;
  transform: translate(calc(-50% - 0.5px), calc(-50% + 0.76px));
}
.neko-msg-peer{display:flex; align-items:center; gap:10px; flex:1; min-width:0}
.neko-msg-peer b{display:block; font-family:"Lora", Georgia, serif; font-weight:600; font-size:16px; color:var(--ink); line-height:1.2}
.neko-msg-peer small{display:block; font-size:12px; color:var(--muted)}
.neko-msg-body{padding:0}
/* liste des conversations */
.conv-list{display:flex; flex-direction:column}
.conv-item{position:relative; padding:14px 18px; border-bottom:1px solid var(--line); cursor:pointer; display:flex; gap:12px; transition:background .15s}
.conv-item:hover{background:var(--cream-2)}
.conv-item b{display:block; font-size:14px; font-family:var(--serif)}
.conv-item .ann{font-family:var(--mono, monospace); font-size:10px; color:var(--terracotta); text-transform:uppercase; letter-spacing:0.06em}
.conv-item .preview{font-size:13px; color:var(--muted); margin-top:2px; line-height:1.35; overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical}
.conv-item .time{font-family:var(--mono, monospace); font-size:10px; color:var(--muted); text-transform:uppercase; margin-top:2px}
.conv-item .unread{width:8px; height:8px; border-radius:50%; background:var(--terracotta); margin-left:auto; margin-top:6px; flex-shrink:0}
/* ⚠️ Ligne « nom + heure », « À propos de … » et pastille chiffrée : structure
   reprise de l'appli (2026-07-27), mais VISIBLE EN MOBILE SEULEMENT. En desktop
   l'item garde son aspect d'origine : le nom seul, l'aperçu, et un simple point
   pour les non-lus, d'où le `font-size:0` qui avale le compteur ici. */
.conv-head{display:flex; align-items:baseline; justify-content:space-between; gap:8px}
.conv-item .time,
.conv-item .ann-app{display:none}
.conv-item .unread{font-size:0}
/* ⚠️ LISTE DE CONVERSATIONS RELEVÉE EN DESKTOP (2026-08-23, demande Julie).
   Le nom (14px) et l'aperçu (13px) sont les tailles du MOBILE, où l'on tient le
   téléphone à trente centimètres ; au bureau la même liste est à un bras, sur un
   écran souvent en zoom navigateur, et l'aperçu — le seul texte qui dit de QUOI
   parle le fil — était le plus petit de la page. On les monte franchement
   (14→17.5 et 13→16, relevés une seconde fois le jour même à la demande de
   Julie), et la ligne de l'aperçu respire d'autant (1.35 → 1.45) : deux lignes
   plus grandes serrées au même interligne se liraient comme un bloc.
   ⚠️ LE NOM EST REDESCENDU LE LENDEMAIN (2026-08-24, demande Julie, en DEUX
   passes le même jour : 17.5 → 16, puis 15) : la Lora grasse dominait la ligne,
   le nom se lisait comme un titre de section. Il passe donc SOUS l'aperçu — la
   graisse et la serif suffisent à le distinguer.
   ⚠️ **PUIS LES DEUX SONT REDESCENDUS ENSEMBLE (2026-08-30, demande Julie :
   « le texte est trop gros, réduis », « je parle en format desktop ») : 15 → 13.5
   et 16 → 14.** Le relevé du 23 avait été fait en DEUX passes le même jour, la
   seconde par-dessus la première : l'aperçu a fini à 3px au-dessus du texte
   courant du site, dans un tiroir de 480px où il s'affiche déjà sur deux lignes
   coupées. ⚠️ **Cette ligne a décrit un aperçu à 16px « qui ne redescend pas » :
   c'est ABROGÉ**, ne pas le restaurer en relisant un texte daté. Ce qui tient,
   c'est le RAPPORT — le nom reste un demi-cran sous l'aperçu, dans les deux
   sens où l'on bouge.
   ⚠️ L'interligne reste à 1.4 : deux lignes serrées au même interligne se
   liraient comme un bloc, c'est ce que le 1.45 du 23 corrigeait — la taille a
   baissé, la raison non.
   ⚠️ **La colonne ÉPINGLÉE (≥1025px) descend AVEC** (`body.neko-msg-pinned`,
   plus bas dans ce fichier) : elle se veut un demi-cran sous le tiroir, la
   laisser à 14.5/15.5 la rendrait PLUS GROSSE que lui. Les deux paires bougent
   ensemble, toujours.
   ⚠️ Le mobile ne bouge PAS (règles ci-dessus, hors media query) : il porte en
   plus l'heure et « À propos de … », l'item y est déjà à trois étages. */
/* ⚠️ **REMONTÉES LE 2026-08-31** (demande Julie : « agrandis le texte de la
   messagerie ») : 13.5/14 → 15/15.5. Le rabaissement du 2026-08-30 (« le texte
   est trop gros, réduis ») est donc ABROGÉ — ne pas le restaurer en relisant le
   pavé ci-dessus, qui garde sa valeur pour le RAPPORT et non pour ses chiffres.
   ⚠️ La colonne épinglée suit dans la même passe (14.5/15) : c'est l'invariant
   du pavé, jamais l'une sans l'autre. */
@media (min-width: 768px){
  .conv-item b{ font-size: 15px; }
  .conv-item .preview{ font-size: 15.5px; line-height: 1.45; }
}
/* fil de discussion */
/* ⚠️ Le fil garde le crème de la MESSAGERIE (`--cream`, celui du tiroir et de la
   liste), il ne se repeint pas. Il était en `--cream-2` (#F5EBDF) : ouvrir une
   conversation assombrissait tout l'écran d'un coup, comme si on avait changé de
   surface alors qu'on reste dans le même panneau. Ce sont les bulles qui portent
   le contraste (blanc + filet pour l'autre, rosé pour soi), pas le fond.
   ⚠️ SANS AUCUNE EXCEPTION depuis le 2026-08-14. La colonne épinglée
   (`body.neko-msg-pinned`) en était une : elle-même en `--cream-2`, elle
   inversait le rapport — fil blanc, bulles beiges. Elle est repassée en
   `--cream`, donc elle applique cette règle-ci comme le tiroir. Ne pas
   réintroduire l'inversion en relisant un texte daté : c'est elle qui
   dessinait un rectangle blanc encastré dans la colonne. */
.neko-msg-thread{display:flex; flex-direction:column; gap:10px; padding:20px 18px 28px; background:var(--cream); min-height:100%}
/* Encadré « rencontre » en tête d'un fil lié à une annonce de garde. Le texte
   dépend du type de garde (cf. `nekoMeetBannerCopy` dans shared.js) : c'est lui
   qui décide qui se déplace, et en home sitting il renvoie vers la visio.
   ⚠️ Posé HORS de `.neko-msg-thread` : le fil est réécrit à chaque envoi. */
/* ⚠️ SANS FOND (2026-08-15, demande Julie) : il était en `--white`, donc une
   carte blanche posée sur le beige du fil, juste sous l'en-tête. Il garde son
   filet, qui le délimite, mais il se fond dans la conversation au lieu de la
   coiffer d'un bloc. Ne pas lui remettre `--white` « pour le détacher » : les
   bulles du fil sont blanches, c'est d'elles qu'il doit se distinguer. */
.neko-meet-banner{display:flex; gap:10px; align-items:flex-start; margin:16px 18px 0; padding:12px; border-radius:14px; background:transparent; border:1px solid var(--line)}
.neko-meet-banner .nmb-ic{width:34px; height:34px; border-radius:10px; background:var(--terracotta); color:#fff; display:grid; place-items:center; flex:0 0 auto}
.neko-meet-banner b{display:block; font-family:"Lora", Georgia, serif; font-weight:600; font-size:14px; color:var(--ink); line-height:1.25}
.neko-meet-banner small{display:block; margin-top:3px; font-size:12px; color:var(--muted); line-height:1.45}
.neko-msg-thread .msg{max-width:78%; padding:10px 14px; border-radius:14px; font-size:14px; line-height:1.45}
.neko-msg-thread .msg.them{background:var(--white); align-self:flex-start; border:1px solid var(--line); border-bottom-left-radius:4px}
/* ⚠️ Bulle de CELUI QUI ÉCRIT — rosé clair sur texte encre, jamais l'inverse.
   Elle était en aplat `--ink` avec texte crème : une brique noire au milieu d'un
   fil beige et blanc, alors que l'assistant (`public/assistant/index.html`) et
   l'appli (`MINE_BUBBLE`, mobile/app/conversation.tsx) posaient déjà la même
   bulle en clair. Les trois surfaces disent maintenant la même chose. */
.neko-msg-thread .msg.me{background:var(--card-rose); color:var(--ink); border:1px solid rgba(31,26,23,.06); align-self:flex-end; border-bottom-right-radius:4px}
/* Message automatique de la plateforme : bulle Neko centrée, même design que
   NekoBubble dans l'app (mobile/app/conversation.tsx) — blanc, en-tête corail
   « Neko · message automatique » avec la patte, corps 13px. */
/* ⚠️ Filet obligatoire depuis que le fil est en `--cream` : un blanc sans bordure
   sur ce crème-là ne se distingue plus, la bulle Neko se dissolvait dans le fond. */
.neko-msg-thread .msg.auto{align-self:center; max-width:88%; background:var(--white); border:1px solid var(--line); border-radius:16px; padding:11px 14px; font-size:13px; line-height:1.45; color:var(--ink)}
.neko-msg-thread .msg-auto-tag{display:flex; align-items:center; gap:5px; margin-bottom:5px; font-size:11px; font-weight:600; color:var(--terracotta)}
.neko-msg-thread .msg-auto-tag svg{flex-shrink:0}
/* Pièces jointes IMAGE d'un message (le ticket de caisse d'une dépense avancée,
   ADR 0054). Vignettes carrées cliquables, ouvertes en grand par la lightbox
   partagée. `object-fit:cover` : un ticket est un rectangle très haut, laissé
   libre il ferait à lui seul la hauteur du fil et pousserait le message hors de
   l'écran — c'est la preuve qu'on montre, pas la photo qu'on expose. */
.neko-msg-thread .msg-photos{display:flex; flex-wrap:wrap; gap:6px; margin-top:8px}
.neko-msg-thread .msg-photo{width:72px; height:72px; object-fit:cover; border-radius:10px; border:1px solid rgba(31,26,23,.10); cursor:zoom-in; display:block}
.neko-msg-thread .msg-meta{font-family:var(--mono, monospace); font-size:10px; color:var(--muted); text-transform:uppercase; align-self:center; margin:4px 0 6px}
/* Action sous un message auto d'urgence véto. Côté owner elle s'exécute sur
   place (il n'a pas l'onglet Urgence) ; côté sitter c'est un lien vers /urgence. */
.neko-msg-thread .msg-cta{align-self:flex-start; margin-top:-4px; padding:8px 14px; border:0; border-radius:999px; background:var(--terracotta); color:#fff; font-size:12.5px; font-weight:700; font-family:inherit; text-decoration:none; line-height:1; cursor:pointer}
.neko-msg-thread .msg-cta.me{align-self:flex-end}
.neko-msg-thread .msg-cta.auto{align-self:center}
.neko-msg-thread .msg-cta:hover{background:var(--terracotta-deep)}
.neko-msg-thread .msg-cta:disabled{background:var(--line); color:var(--muted); cursor:default}
.neko-msg-thread .msg-vet-err{align-self:flex-start; max-width:78%; margin:0; font-size:12px; color:var(--terracotta)}
/* Panneau de règlement du devis, monté à la place du bouton */
.neko-msg-thread .msg-vet-pay{align-self:stretch; background:var(--white); border:1px solid var(--line); border-radius:14px; padding:14px}
.neko-msg-thread .msg-vet-row{display:flex; justify-content:space-between; gap:12px; align-items:baseline; font-size:13px; color:var(--muted); padding:3px 0}
.neko-msg-thread .msg-vet-row.is-sum{margin-top:6px; padding-top:8px; border-top:1px solid var(--line); color:var(--ink); font-weight:700}
.neko-msg-thread .msg-vet-row.is-sum b{font-size:17px}
.neko-msg-thread .msg-vet-el{margin:12px 0}
.neko-msg-thread .msg-vet-pay .msg-cta{align-self:auto; display:block; width:100%; margin-top:4px; padding:11px; text-align:center}

/* ---- Réactions emoji sur un message (❤️ 🙏 😂 😮) --------------------------
   Chaque message devient une LIGNE (.msg-row) qui porte trois choses : la bulle,
   ses pastilles de réaction dessous, et le bouton « réagir » du desktop à côté.

   ⚠️ La bulle reste `.msg`, au même niveau de nommage qu'avant : tout le CSS
   existant la vise en descendant (colonne épinglée, bulle auto, panneau véto),
   et la déplacer d'un cran dans l'arbre ne casse donc rien. Les `align-self`
   de `.msg.me/.them` continuent de fonctionner : dans `.msg-col`, qui est une
   colonne flex, ils commandent l'axe horizontal exactement comme avant.

   ⚠️ Les bulles NEKO (`.msg.auto`) ne sont PAS enveloppées : elles restent
   enfant direct du fil, centrées, sans réaction possible. Un bouton à côté d'une
   bulle centrée décale son centrage.

   Le bouton n'apparaît qu'au SURVOL (`hover:hover`) : au doigt, le geste est
   l'appui long. Un écran tactile sans souris rapporte `hover:none` et ne
   réserve donc aucune place à un bouton qu'il ne pourrait jamais révéler. */
/* iOS : pas de menu système « Copier / Rechercher » par-dessus la palette. ⚠️ La
   SÉLECTION du texte reste possible (pas de `user-select:none`) : c'est par la
   messagerie que les deux parties s'échangent leur adresse, et une bulle qu'on
   ne peut plus sélectionner est une adresse qu'on doit recopier à la main. */
.neko-msg-thread .msg{-webkit-touch-callout:none}
.neko-msg-thread .msg-row{display:flex; align-items:flex-end; gap:4px; max-width:86%}
.neko-msg-thread .msg-row.them{align-self:flex-start}
.neko-msg-thread .msg-row.me{align-self:flex-end; flex-direction:row-reverse}
.neko-msg-thread .msg-col{display:flex; flex-direction:column; min-width:0; max-width:100%}
.neko-msg-thread .msg-row .msg{max-width:100%}
/* Les pastilles chevauchent le bas de la bulle (marge négative), comme dans les
   messageries natives : posées en dessous avec un vrai écart, elles se lisent
   comme un nouveau message. `:empty` → aucune place réservée sans réaction. */
.neko-msg-thread .msg-reacts{display:flex; flex-wrap:wrap; gap:4px; margin-top:-9px; padding:0 8px; position:relative; z-index:1}
.neko-msg-thread .msg-row.me .msg-reacts{align-self:flex-end}
.neko-msg-thread .msg-reacts:empty{display:none}
.neko-msg-thread .msg-react-pill{display:inline-flex; align-items:center; gap:3px; padding:2px 8px; border-radius:999px; border:1px solid var(--line); background:var(--white); font-size:13px; line-height:1.45; font-family:inherit; cursor:pointer; box-shadow:0 1px 3px rgba(31,26,23,.08)}
.neko-msg-thread .msg-react-pill.is-mine{border-color:var(--terracotta); background:var(--card-rose)}
.neko-msg-thread .msg-react-pill i{font-style:normal; font-size:11px; font-weight:700; color:var(--muted)}
.neko-msg-thread .msg-react-btn{flex:0 0 auto; width:26px; height:26px; margin-bottom:3px; padding:0; border:0; border-radius:50%; background:transparent; color:var(--muted); cursor:pointer; display:none; place-items:center; opacity:0; transition:opacity .15s ease, color .15s ease}
.neko-msg-thread .msg-react-btn svg{width:16px; height:16px}
@media (hover:hover){
  .neko-msg-thread .msg-react-btn{display:grid}
  .neko-msg-thread .msg-row:hover .msg-react-btn,
  .neko-msg-thread .msg-react-btn:focus-visible{opacity:1}
  .neko-msg-thread .msg-react-btn:hover{color:var(--terracotta); background:var(--white)}
}

/* Palette d'appui long. Posée sur le <body> et non dans le tiroir : celui-ci
   porte un `transform`, qui ferait de lui le référentiel de tout `position:fixed`
   à l'intérieur — la palette se placerait alors par rapport au tiroir en croyant
   se placer par rapport à l'écran. z-index au-dessus du tiroir (130). */
/* ⚠️ z-index AU-DESSUS de la visionneuse (4000) depuis le 2026-08-31 : le bouton
   qui l'ouvre s'y trouve désormais (cf. `.neko-lb-react`), et à 140 la palette
   s'ouvrait DERRIÈRE la photo — invisible, alors qu'elle avait bien répondu au
   clic. C'est un popover transitoire, il est légitime qu'il couvre tout. */
.neko-react-pop{position:fixed; top:0; left:0; z-index:4100; display:none; align-items:center; gap:2px; padding:5px; border-radius:999px; background:var(--white); border:1px solid var(--line); box-shadow:0 12px 34px -10px rgba(31,26,23,.34)}
.neko-react-pop.open{display:flex}
.neko-react-pop button{width:40px; height:40px; padding:0; border:0; border-radius:50%; background:transparent; font-size:21px; line-height:1; font-family:inherit; cursor:pointer; display:grid; place-items:center; transition:transform .12s ease, background .12s ease}
.neko-react-pop button:hover{background:var(--cream-2); transform:scale(1.14)}
.neko-react-pop button.is-on{background:var(--card-rose)}
/* « Copier » : l'appui long servait à copier le texte avant d'ouvrir cette
   palette. On lui reprend le geste, on lui rend l'action — c'est par la
   messagerie que les deux parties s'échangent leur adresse. */
.neko-react-pop .neko-react-copy{width:auto; min-width:74px; padding:0 12px; margin-left:3px; border-left:1px solid var(--line); border-radius:0 999px 999px 0; font-size:12.5px; font-weight:700; color:var(--muted)}
.neko-react-pop .neko-react-copy:hover{transform:none; color:var(--ink)}

/* ---- Réactions d'une JOURNÉE du journal de garde (❤️ 😻 🙏 😂 😮) ----------
   Portées par /mon-espace (carte du jour) et /journal-garde (bloc daté), donc
   ici et pas dans l'une des deux pages : la même ligne y est rendue par le même
   helper de shared.js (`nekoJournalReactRowHtml`).
   ⚠️ Le bouton est TOUJOURS visible, contrairement à celui de la messagerie qui
   n'apparaît qu'au survol : le journal se lit d'abord au doigt, et un survol ne
   se produit jamais sur un téléphone. Sans lui, le geste n'existerait pas là où
   le propriétaire regarde ses photos. */
/* Repli d'une vidéo illisible par ce navigateur (cf. nekoVideoFallback).
   Volontairement discret : c'est une contrariété, pas une alerte. */
.neko-video-fallback{display:block; margin-top:12px; padding:14px 16px; border-radius:14px;
  background:var(--cream-2); color:var(--muted); font-size:12.5px; line-height:1.45;
  text-align:center; text-decoration:none}
.neko-video-fallback:hover{color:var(--ink)}
.jr-row{display:flex; align-items:center; flex-wrap:wrap; gap:6px; margin-top:12px}
.jr-pills{display:flex; flex-wrap:wrap; gap:4px}
.jr-pills:empty{display:none}
.jr-pill{display:inline-flex; align-items:center; gap:3px; padding:3px 9px; border-radius:999px; border:1px solid var(--line); background:var(--white); font-size:13.5px; line-height:1.45; font-family:inherit; cursor:pointer; box-shadow:0 1px 3px rgba(31,26,23,.08)}
.jr-pill.is-mine{border-color:var(--terracotta); background:var(--card-rose)}
.jr-pill i{font-style:normal; font-size:11px; font-weight:700; color:var(--muted)}
/* ⚠️ Corail au repos, pas seulement au survol : ces deux boutons sont les seuls
   gestes offerts au propriétaire sur une journée, et un survol ne se produit
   jamais sur un téléphone, où le journal se lit d'abord. En gris ils passaient
   pour de la décoration. Corail = `--terracotta` sur le texte et le filet ;
   `--terracotta-deep` reste réservé aux fonds. */
.jr-add{flex:0 0 auto; width:28px; height:28px; padding:0; border:1px solid var(--terracotta); border-radius:50%; background:var(--white); color:var(--terracotta); cursor:pointer; display:grid; place-items:center; transition:background-color .15s ease}
.jr-add svg{width:16px; height:16px}
.jr-add:hover{background:var(--card-rose)}
/* ⚠️ Sans cette ligne, le `display:grid` ci-dessus l'emporte sur le
   `[hidden]{display:none}` du navigateur et le bouton resterait visible alors
   qu'on a déjà posé son emoji (même piège que `.neko-msg-compose[hidden]`). */
.jr-add[hidden]{display:none}

/* ---- Réponses du PROPRIÉTAIRE à une journée (2026-08-08) -------------------
   Rendues par `nekoJournalReplyRowHtml` (shared.js), donc portées par les deux
   pages : /mon-espace (carte du jour) et /journal-garde (bloc daté).
   ⚠️ Le liseré corail à gauche n'est pas décoratif : c'est ce qui distingue une
   RÉPONSE d'une note du sitter dans un journal qui, autrement, empile des
   paragraphes tous pareils. Côté sitter, c'est le seul signal qu'on lui parle.
   ⚠️ `white-space:pre-line` : le propriétaire écrit dans une zone de texte, ses
   retours à la ligne sont des retours à la ligne. */
/* ⚠️ Le BOUTON n'est pas ici, il est dans `.jr-row` : « Répondre » et l'emoji
   sont les deux gestes du même moment et tiennent sur la même ligne. Ce bloc-ci
   ne porte plus que les mots déjà écrits — et reste monté VIDE côté propriétaire
   (ancre du redessin), d'où le `:empty` qui lui retire sa place. */
.jrep-row{display:flex; flex-direction:column; align-items:flex-start; gap:8px; margin-top:10px}
.jrep-row:empty{display:none}
.jrep-item{width:100%; box-sizing:border-box; padding:9px 12px; border-left:3px solid var(--terracotta);
  border-radius:0 10px 10px 0; background:var(--cream)}
.jrep-item b{display:block; font-size:12px; font-weight:700; letter-spacing:.02em; color:var(--terracotta)}
.jrep-item span{display:block; margin-top:2px; font-size:13.5px; line-height:1.5; color:var(--ink); white-space:pre-line}
/* ⚠️ **CTA corail PLEIN**, comme tous les boutons d'action du site (relief 3D
   ajouté par le bloc « CTA CORAIL » plus bas, où la classe est listée) : c'est
   la seule ACTION de la ligne, et en contour elle se lisait comme une variante
   du bouton emoji d'à côté. Ils ne sont plus deux gestes du même poids — l'emoji
   se retire d'un clic, un mot part au sitter. ⚠️ Et ils ne cohabitent que dans
   UN état : le bouton emoji disparaît dès qu'une réaction est posée. */
/* ⚠️ `margin-left:auto` le pousse au BOUT de la ligne, comme dans l'app
   (`JournalDayActions`, même valeur) : collé aux pastilles il se lisait comme
   une réaction de plus, alors que c'est l'action de la journée. Il reste dans
   `.jr-row`, donc si la ligne se replie il part à droite de sa propre ligne. */
.jrep-add{flex:0 0 auto; margin-left:auto; padding:6px 16px; border:1px solid var(--ink); border-radius:6px;
  background:var(--terracotta); font-family:inherit; font-size:12.5px; font-weight:700; color:#fff;
  cursor:pointer}
/* bandeau « Choisir ce sitter » dans une conversation owner↔sitter — même fond
   crème que le fil (.neko-msg-thread, var(--cream)) : en blanc + filet, il
   dessinait une barre rapportée en travers de la conversation. ⚠️ Il suit le fil,
   pas l'inverse : les deux valeurs se lisent ensemble. */
.neko-choose-banner{padding:12px 16px; background:var(--cream); display:flex; justify-content:center}
/* zone de saisie */
.neko-msg-compose{display:flex; gap:10px; padding:14px 16px; padding-bottom:calc(14px + env(safe-area-inset-bottom)); border-top:1px solid var(--line); flex-shrink:0; background:var(--cream)}
/* En vue liste la barre d'envoi est masquée (attribut hidden) : sans cette règle,
   le display:flex ci-dessus l'emporterait sur [hidden]{display:none} et laisserait
   un champ « Votre message… » parasite au-dessus du bouton « Parlez à Berlioz ». */
.neko-msg-compose[hidden]{display:none}
.neko-msg-compose input{flex:1; padding:10px 14px; border:1px solid var(--line); border-radius:999px; outline:none; background:var(--cream-2); font-size:14px}
/* Footer « Parlez à Berlioz » du tiroir messagerie — épinglé en bas de la
   vue liste (masqué dans un fil de conversation). Miroir du bouton de l'app. */
.neko-msg-cta{flex-shrink:0; padding:12px 16px calc(12px + env(safe-area-inset-bottom)); border-top:1px solid var(--line); background:var(--cream)}
.neko-msg-cta-btn{display:flex; align-items:center; gap:12px; width:100%; text-decoration:none; border:1px solid var(--ink); border-radius:10px; background:var(--terracotta); color:#fff; padding:12px 14px; box-shadow:0 3px 0 #C44C3D; transition:transform .12s ease, box-shadow .12s ease}
.neko-msg-cta-btn:active{transform:translateY(3px); box-shadow:0 0 0 #C44C3D}
.neko-msg-cta-ic{width:38px; height:38px; border-radius:11px; border:2px solid var(--ink); background:#fff; display:grid; place-items:center; flex:0 0 auto}
.neko-msg-cta-ic svg{width:18px; height:18px; color:#C44C3D}
.neko-msg-cta-tx{flex:1; min-width:0}
.neko-msg-cta-tx b{display:block; font-family:"Lora", Georgia, serif; font-weight:600; font-size:15px; color:#fff}
.neko-msg-cta-tx small{display:block; font-size:12px; color:rgba(255,255,255,.9); margin-top:1px}
.neko-msg-cta-chev{font-size:18px; color:rgba(255,255,255,.85); flex:0 0 auto}
.nt .nt-ic{width:38px; height:38px; border-radius:11px; background:var(--cream-2); display:grid; place-items:center; color:var(--terracotta); flex-shrink:0}
.nt b{font-size:14px; font-family:"Lora", Georgia, serif; font-weight:600; display:block}
.nt small{display:block; color:var(--muted); font-size:12px; margin-top:2px; line-height:1.45}
.nt .nt-time{font-family:var(--mono); font-size:9px; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); margin-top:6px}

/* ---------- BUTTONS (CTA principal corail comme la landing) ---------- */
.btn{display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:14px 26px; border-radius:6px; font-size:15px; font-weight:600; letter-spacing:0.01em; transition: transform .15s, background .2s, border-color .2s, color .2s; cursor:pointer; min-height:48px; white-space:nowrap}
.btn-primary{background:var(--terracotta); color:#fff; box-shadow: var(--shadow)}
.btn-primary:hover{background:var(--terracotta-deep); transform:translateY(-1px)}
.btn-ghost{border:1.5px solid var(--terracotta); color:var(--terracotta); background:transparent}
.btn-ghost:hover{background:var(--terracotta); color:#fff}
.btn-subtle{background:var(--cream-2); color:var(--ink); border:1px solid var(--line)}
.btn-subtle:hover{background:var(--white)}
.btn-role{background: var(--role, var(--terracotta-deep)); color:var(--white)}
.btn-role:hover{background: var(--role-deep, var(--terracotta-deep))}
.btn-danger{color:var(--danger); border:1px solid color-mix(in oklab, var(--danger) 40%, transparent)}
.btn-danger:hover{background:var(--danger); color:var(--white); border-color:var(--danger)}
.btn-block{width:100%}
.btn-lg{padding:14px 28px; font-size:15px}
.btn-sm{padding:8px 14px; font-size:13px}

/* ---------- FORM ---------- */
.field{display:flex; flex-direction:column; gap:6px; margin-bottom:18px}
.field label{font-family:var(--mono); font-size:10px; letter-spacing:0.1em; text-transform:uppercase; color:var(--muted); font-weight:500}
.field .help{font-size:12px; color:var(--muted); margin-top:-2px}
.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="number"],
.field input[type="date"],
.field input[type="tel"],
.field textarea,
.field select{
  padding: 12px 14px; border:1px solid var(--line); border-radius:10px;
  background:var(--white); font-size:15px; color:var(--ink);
  transition: border-color .2s, box-shadow .2s;
  outline:none; width:100%;
}
.field input:focus, .field textarea:focus, .field select:focus{
  border-color: var(--terracotta);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--terracotta) 18%, transparent);
}
.field textarea{min-height: 110px; resize:vertical; font-family:inherit; line-height:1.5}
.field.row-2{display:grid; grid-template-columns:1fr 1fr; gap:14px}
@media (max-width:380px){ .field.row-2{grid-template-columns:1fr} }

/* ---------- Afficher / masquer un mot de passe ----------
   Le balisage est injecté par nekoPasswordReveal() (shared.js) : on n'écrit
   jamais .pwd-wrap ni .pwd-toggle à la main dans une page.
   ⚠️ Le bouton se superpose au champ, il ne se pose pas à côté : posé à côté,
   il rétrécirait l'input sur les grilles .field.row-2 et sur mobile. D'où la
   réserve de padding à droite, qui doit rester supérieure à la largeur du
   bouton, sinon le texte saisi passe dessous. */
.pwd-wrap{position:relative; display:block; width:100%}
.pwd-wrap > input{padding-right:46px}
.pwd-toggle{
  position:absolute; top:0; bottom:0; right:5px; margin:auto 0;
  width:36px; height:36px; padding:0; border:0; background:transparent;
  display:flex; align-items:center; justify-content:center;
  border-radius:8px; cursor:pointer; color:var(--muted);
  transition: color .2s, background .2s;
}
.pwd-toggle:hover{color:var(--ink); background:var(--cream-2)}
.pwd-toggle:focus-visible{outline:2px solid var(--terracotta); outline-offset:2px}
.pwd-toggle svg{width:20px; height:20px; display:block; pointer-events:none}

/* checkbox card + radio card */
.check-group, .radio-group{display:grid; gap:10px}
.check-group.cols-2, .radio-group.cols-2{grid-template-columns:1fr 1fr}
.check-group.cols-3, .radio-group.cols-3{grid-template-columns:repeat(3,1fr)}
@media (max-width:620px){ .check-group.cols-2, .check-group.cols-3, .radio-group.cols-2, .radio-group.cols-3{grid-template-columns:1fr} }
.choice{
  display:flex; align-items:flex-start; gap:12px;
  padding:14px 16px; border:1px solid var(--line); border-radius:12px;
  background:var(--white); cursor:pointer;
  transition: border-color .2s, background .2s;
}
.choice:hover{border-color:var(--terracotta)}
.choice input{position:absolute; opacity:0; pointer-events:none}
.choice input:checked ~ .choice-dot{background:var(--terracotta); border-color:var(--terracotta)}
.choice input:checked ~ .choice-dot::after{opacity:1}
.choice:has(input:checked){border-color:var(--terracotta); background:color-mix(in oklab, var(--terracotta) 8%, var(--white))}
.choice-dot{
  width:18px; height:18px; border-radius:50%; border:1.5px solid var(--line);
  flex-shrink:0; margin-top:2px; position:relative; transition: all .15s;
}
.choice-dot::after{content:""; position:absolute; inset:4px; border-radius:50%; background:var(--white); opacity:0; transition: opacity .15s}
.choice-dot.square{border-radius:5px}
.choice-dot.square::after{border-radius:2px}
.choice-body b{display:block; font-size:14px; font-weight:500}
.choice-body span{display:block; font-size:13px; color:var(--muted); margin-top:2px}

/* ---------- CARDS / CHIPS ---------- */
.card{background:var(--white); border:none; border-radius: var(--radius-lg); padding: 24px}
.card-lift{box-shadow: var(--shadow)}
.chip{display:inline-flex; align-items:center; gap:6px; padding:6px 12px; border-radius:999px; border:1px solid var(--line); background:var(--white); font-size:12px; color:var(--ink-soft); transition:all .2s}
.chip:hover{border-color:var(--ink); color:var(--ink)}
.chip.active{background:var(--ink); color:var(--cream); border-color:var(--ink)}
.tag{font-family:var(--mono); font-size:10px; letter-spacing:0.05em; text-transform:uppercase; padding: 3px 8px; border-radius:4px; background:var(--cream-2); color:var(--ink-soft)}
.tag-sage{color:var(--sage-deep); background: color-mix(in oklab, var(--sage) 18%, var(--white))}
.tag-terracotta{color:var(--terracotta); background: color-mix(in oklab, var(--terracotta) 18%, var(--white))}
.tag-ink{color:var(--cream); background:var(--ink)}

/* status pills */
.pill{display:inline-flex; align-items:center; gap:6px; padding:4px 10px; border-radius:999px; font-family:var(--mono); font-size:10px; letter-spacing:0.08em; text-transform:uppercase}
.pill::before{content:""; width:6px; height:6px; border-radius:50%}
.pill-wait{background:#F4E6D0; color:#8A6840} .pill-wait::before{background:#C8994A}
.pill-ok{background: color-mix(in oklab, var(--sage) 22%, var(--white)); color:var(--sage-deep)} .pill-ok::before{background:var(--sage-deep)}
.pill-done{background: var(--cream-3); color:var(--ink-soft)} .pill-done::before{background:var(--muted)}
.pill-new{background: color-mix(in oklab, var(--terracotta) 22%, var(--white)); color:var(--terracotta)} .pill-new::before{background:var(--terracotta)}
.pill-reject{background:#F4D8D2; color:#8A3A2E} .pill-reject::before{background:#B84A3A}
/* Pastille de type posée SUR une photo de carte (generateCards, sdTypePill,
   hsPaintAds) : bornée à la largeur de la photo moins ses marges de 12 px,
   et le texte passe à la ligne au lieu de déborder — « 2 VISITES/JOUR ·
   30 MIN » sortait de la carte en format tablette. L'ancre `right: 12px`
   fait le reste : la pastille grandit vers la gauche, jamais au-delà. */
.pill-clip{max-width:calc(100% - 24px); flex-wrap:wrap; text-align:center; justify-content:center}

/* ---------- BADGES D'AVIS (owner → sitter, FR20) ---------- */
/* Lecture : chips posées sous un avis, ou agrégées sur le profil du sitter.
   Saisie : .neko-badge-pick (cases à cocher déguisées) dans /laisser-notation. */
.neko-badges{display:flex; flex-wrap:wrap; gap:7px; margin-top:10px}
.neko-badge{display:inline-flex; align-items:center; gap:7px; padding:6px 13px; border-radius:999px;
  font-size:12px; font-weight:500; line-height:1.3; letter-spacing:.005em;
  background: color-mix(in oklab, var(--terracotta) 8%, var(--white));
  border:1px solid color-mix(in oklab, var(--terracotta) 20%, var(--white));
  color:var(--terracotta)}
/* ⚠️ Le compteur « ×N » et sa pastille (`.neko-badge--count b`) sont RETIRÉS
   (2026-08-30) : la chip ne porte plus que le libellé, d'où le `padding`
   symétrique ci-dessus — l'ancien `padding-right: 6px` réservait la place de la
   pastille. Restaurer l'un sans l'autre donne une chip collée à droite. */

/* Bandeau d'aperçu design (?badges_demo=1) : doit être impossible à confondre
   avec du contenu réel, d'où le fond ambré et le ton d'avertissement. */
.pf-demo-notice{margin:0 0 14px; padding:10px 13px; border-radius:10px; font-size:12.5px; line-height:1.45;
  background:#F7EAD2; color:#7A5A24}
.pf-demo-notice code{font-family:var(--mono); font-size:11.5px; background:rgba(0,0,0,.06); padding:1px 5px; border-radius:4px}

/* Sur une carte de recherche, l'espace est compté : chips plus compactes. */
.card-sitter .neko-badges{margin-top:8px}
.card-sitter .neko-badge{font-size:11px; padding:4px 9px}

.neko-badge-grid{display:flex; flex-wrap:wrap; gap:8px; margin-top:8px}
.neko-badge-fam{font-family:var(--mono); font-size:10px; letter-spacing:0.08em; text-transform:uppercase;
  color:var(--muted); margin:16px 0 0}
.neko-badge-fam:first-child{margin-top:4px}
.neko-badge-pick{display:inline-flex; align-items:center; gap:7px; padding:8px 13px; border-radius:999px;
  border:1px solid var(--line); background:var(--white); font-size:13px; color:var(--ink-soft);
  cursor:pointer; transition:border-color .15s, background .15s, color .15s; user-select:none}
.neko-badge-pick input{position:absolute; opacity:0; pointer-events:none}
.neko-badge-pick:hover{border-color:var(--ink-soft)}
.neko-badge-pick.on{background: color-mix(in oklab, var(--sage) 18%, var(--white));
  border-color:var(--sage-deep); color:var(--sage-deep); font-weight:600}
/* Au-delà de la limite, les non-cochés se grisent : la contrainte se voit avant
   le clic plutôt que de renvoyer un message d'erreur après coup. */
.neko-badge-pick.off{opacity:.4; cursor:not-allowed}

/* verified badge */
.badge-verified{display:inline-flex; align-items:center; gap:6px; padding: 5px 10px; border-radius: 999px; font-size: 11px; font-weight:600; background:var(--white); border:1px solid var(--line)}
.badge-verified .check{width:12px; height:12px; border-radius:50%; background: var(--sage); display:grid; place-items:center; color:var(--white); font-size:8px; font-weight:bold}

/* ---------- FIL D'ARIANE (canonique) ----------
   ⚠️ C'EST LE SEUL REPÈRE DE NAVIGATION DES PAGES DU SITE (décision 2026-07-30).
   Aucune page ne porte plus de flèche « retour » : une flèche ne dit pas où elle
   mène, elle double le bouton retour du navigateur, et sur les 24 pages qui en
   avaient une c'était 24 en-têtes locales à maintenir (`.vet-head`, `.cid-topbar`,
   `.bk-topbar`…) au lieu d'un style partagé. Une nouvelle page se repère avec ce
   fil d'Ariane, jamais avec `history.back()`.
   ⚠️ Le style est AUTONOME (`.breadcrumb` seul, sans ancêtre `.page-head`) :
   la plupart des pages concernées ont un en-tête local, pas un `.page-head`. */
.breadcrumb{font-family:var(--mono); font-size:10px; letter-spacing:0.1em; text-transform:uppercase; color:var(--muted); display:flex; flex-wrap:wrap; gap:8px; align-items:center}
/* Zone tactile ~26px sans décaler la ligne : en 10px mono, le lien seul serait
   trop petit à toucher au pouce. Le `margin` négatif annule exactement le
   `padding`, donc rien ne bouge visuellement. */
.breadcrumb a{padding:8px 0; margin:-8px 0}
.breadcrumb a:hover{color:var(--terracotta)}

/* ---------- PAGE HEAD (titre serif italique comme la landing) ---------- */
.page-head{padding: 64px 0 40px; border-bottom:1px solid var(--line)}
.page-head .breadcrumb{margin-bottom: 14px}
.page-head h1{font-size: clamp(30px, 4vw, 44px); margin-bottom: 10px; font-family:"Lora", Georgia, serif; font-weight:600; letter-spacing:-0.018em; line-height:1.05}
.page-head h1 em{color:var(--terracotta); font-weight:400}
.page-head .lede{margin-top:12px; max-width: 600px}

/* ---------- ACCORDEON FAQ ----------
   Rendu par defaut de `.faq > details` sur les pages publiques. Il vivait
   uniquement dans le <style> local de /faq, recopie en variante dans l'article
   de blog : la 3e page qui en avait besoin (/home-sitting) l'aurait recopie une
   fois de plus. Les deux blocs locaux existants sont laisses en place et gagnent
   par ordre de cascade (ou par specificite pour `.article .faq`) : ils sont
   identiques a ces regles, donc rien ne bouge visuellement. Toute page neuve se
   contente desormais de `class="faq"`. */
.faq details{border-bottom:1px solid var(--line); padding: 22px 0}
.faq summary{font-family:var(--display); font-weight:700; font-size:19px; cursor:pointer; list-style:none; display:flex; justify-content:space-between; align-items:center; gap:12px; color:var(--ink)}
/* `list-style:none` ne suffit pas sur Safari/iOS, qui garde son triangle. */
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+"; font-size:24px; color:var(--terracotta); transition: transform .2s}
.faq details[open] summary::after{transform: rotate(45deg)}
.faq p{color:var(--ink-soft); margin: 14px 0 0; line-height:1.6}

/* ---------- FOOTER (aligne sur la landing : ink sombre + accents corail) ---------- */
.foot{padding: 64px 0 36px; background: var(--ink); color: color-mix(in oklab, var(--cream) 70%, transparent); font-size:13px; margin-top: 96px}
.foot-grid{display:grid; grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr; gap: 40px; padding-bottom: 32px; border-bottom:1px solid color-mix(in oklab, var(--cream) 12%, transparent)}
@media (max-width: 820px){ .foot-grid{grid-template-columns: 1fr 1fr; gap:28px} }
.foot-grid h5{font-family:var(--mono); font-size:11px; letter-spacing:0.1em; text-transform:uppercase; color: var(--cream); margin: 0 0 14px; font-weight:500}
.foot-grid ul{list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:8px}
.foot-grid a{transition: color .2s}
.foot-grid a:hover{color: var(--terracotta)}
.foot-brand{color:var(--cream)}
.foot-brand p{margin: 10px 0 0; max-width: 300px; font-size:13px; line-height:1.5}
.foot-bottom{display:flex; justify-content:space-between; padding-top: 24px; font-family:var(--mono); font-size:10px; letter-spacing:0.08em; text-transform:uppercase; color: color-mix(in oklab, var(--cream) 50%, transparent); flex-wrap:wrap; gap:12px}

/* ---------- UTILITIES ---------- */
.flex{display:flex} .flex-col{display:flex; flex-direction:column} .gap-2{gap:8px} .gap-3{gap:12px} .gap-4{gap:16px} .gap-5{gap:20px} .gap-6{gap:24px}
.items-center{align-items:center} .items-start{align-items:flex-start} .items-end{align-items:flex-end}
.justify-between{justify-content:space-between} .justify-center{justify-content:center}
.grid{display:grid} .grid-2{grid-template-columns:1fr 1fr} .grid-3{grid-template-columns:repeat(3,1fr)} .grid-4{grid-template-columns:repeat(4,1fr)}
@media (max-width:820px){.grid-3,.grid-4{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}}
.mt-2{margin-top:8px} .mt-3{margin-top:12px} .mt-4{margin-top:16px} .mt-5{margin-top:20px} .mt-6{margin-top:24px} .mt-8{margin-top:32px} .mt-10{margin-top:40px} .mt-12{margin-top:48px}
.mb-2{margin-bottom:8px} .mb-3{margin-bottom:12px} .mb-4{margin-bottom:16px} .mb-6{margin-bottom:24px} .mb-8{margin-bottom:32px}
.text-muted{color:var(--muted)} .text-soft{color:var(--ink-soft)} .text-terra{color:var(--terracotta)} .text-sage{color:var(--sage-deep)}
.text-sm{font-size:13px} .text-xs{font-size:12px} .text-lg{font-size:17px}
.hide-mobile{} @media (max-width:720px){.hide-mobile{display:none!important}}

/* ---------- SCROLL REVEAL (optional) ---------- */
.reveal{opacity:0; transform:translateY(12px); transition: opacity .6s ease, transform .6s ease}
.reveal.in{opacity:1; transform:translateY(0)}

/* selection */
::selection{background: var(--terracotta); color: var(--cream)}

/* avatar placeholder */
.avatar{display:grid; place-items:center; font-family:var(--serif); font-style:italic; color:var(--white); border-radius:50%; flex-shrink:0}
.avatar.sm{width:32px; height:32px; font-size:14px}
.avatar.md{width:44px; height:44px; font-size:18px}
.avatar.lg{width:72px; height:72px; font-size:30px}
.avatar.xl{width:120px; height:120px; font-size:48px}
/* Avatar PHOTO : l'initiale reste le repli, la photo la remplace quand elle
   existe (messagerie : photo du sitter côté owner, photo du chat côté sitter).
   `overflow:hidden` + `object-fit:cover` pour que l'image épouse le cadre sans
   se déformer, quel que soit son format d'origine.
   ⚠️ `border-radius:inherit` et JAMAIS `50%` en dur : le cadre n'est pas
   toujours un rond. Le héro de `/profil-proprietaire` (et celui de
   `/profil-sitter`) pose un carré arrondi de 16px — avec un `50%` figé,
   la photo restait un disque à l'intérieur et le dégradé corail du fond
   ressortait aux quatre coins, comme un cadre qu'on n'avait pas demandé. */
.avatar.has-photo{overflow:hidden}
.avatar>img{width:100%; height:100%; object-fit:cover; border-radius:inherit; display:block}
/* Avatar et nom de l'interlocuteur mènent à son profil : lien discret (le nom
   garde l'aspect qu'il avait, on ne souligne qu'au survol). */
a.neko-conv-avatar{display:inline-flex; text-decoration:none; flex-shrink:0}
a.neko-conv-name{color:inherit; text-decoration:none}
a.neko-conv-name:hover{text-decoration:underline}

/* "photo" placeholder (gradient + grain) */
.ph{position:relative; overflow:hidden; background: var(--cream-3)}
.ph::after{content:""; position:absolute; inset:0; background: repeating-linear-gradient(135deg, transparent 0 14px, rgba(26,22,20,0.04) 14px 15px); pointer-events:none}
/* le grain diagonal n'est qu'un habillage de placeholder : dès qu'il y a une vraie photo, on l'enlève */
.ph:has(img:not([data-neko-broken]))::after{content:none}
/* Photo introuvable (objet purgé, URL périmée) : shared.js pose data-neko-broken,
   on masque l'image morte pour laisser réapparaître le placeholder qui vit dessous.
   Sans ça, une vignette sur fond sombre se lit comme un rectangle noir. */
img[data-neko-broken]{display:none !important}
.ph .ph-initial{position:absolute; inset:0; display:grid; place-items:center; font-family:var(--serif); font-style:italic; font-size: 80px; color: color-mix(in oklab, var(--ink) 22%, transparent)}
.ph.gradient-1{background: linear-gradient(160deg, #E6DCC9 0%, #D4B89A 50%, #CC3A20 100%)}
.ph.gradient-2{background: linear-gradient(180deg, #C9D4C2 0%, #999999 55%, #555555 100%)}
.ph.gradient-3{background: linear-gradient(200deg, #F0C9A8 0%, #ff6262 60%, #A02810 100%)}
.ph.gradient-4{background: linear-gradient(150deg, #2A2420 0%, #3A342E 40%, #777777 100%)}
.ph.gradient-5{background: linear-gradient(170deg, #EFE7DA 0%, #C9B5A0 100%)}

/* ============================================================
   CTA — bouton 3D corail (style hero généralisé, pages HTML)
   Fill corail, liseré encre décalé (::before), tranche 3D coralInk
   verticale (#C44C3D), s'enfonce au clic. Même look que le bouton
   du hero. Ciblé sur les CTA PRIMAIRES : btn-primary + nav-cta.
   Les boutons ghost / secondaires gardent leur contour.
   ============================================================ */
.btn-primary,
.nav-links a.nav-cta{
  position: relative;
  border: 1px solid transparent;
  border-radius: 26px;
  box-shadow: 0 1px 0 0 #C44C3D;
  transition: transform .1s ease, box-shadow .1s ease, background .2s, color .2s;
  will-change: transform;
}
/* Liseré encre décalé vers le haut (trait dessiné) */
.btn-primary::before,
.nav-links a.nav-cta::before{
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid #1F1A1A;
  border-radius: inherit;
  transform: translateY(-3px);
  pointer-events: none;
}
.btn-primary:hover,
.nav-links a.nav-cta:hover{
  background: var(--terracotta);
  transform: translateY(-1px);
  box-shadow: 0 2px 0 0 #C44C3D;
}
.btn-primary:active,
.nav-links a.nav-cta:active{
  transform: translateY(1px);
  box-shadow: 0 0 0 0 #C44C3D;
}
@media (prefers-reduced-motion: reduce){
  .btn-primary, .nav-links a.nav-cta{
    transition: background .2s, color .2s;
  }
  .btn-primary:hover, .nav-links a.nav-cta:hover,
  .btn-primary:active, .nav-links a.nav-cta:active{
    transform: none;
  }
}
/* Le nav-cta devient un cercle plein sur mobile : pas de relief 3D ni liseré. */
@media (max-width: 767px){
  .nav-links a.nav-cta,
  .nav-links a.nav-cta:hover,
  .nav-links a.nav-cta:active{ box-shadow: none; transform: none; }
  .nav-links a.nav-cta::before{ display: none; }
}

/* ===========================================================================
   NAVIGATION « app-like » — menu vertical (Montée par shared.js quand connecté).
   - Format appli (`body.neko-mobile-shell`), LES DEUX RÔLES : ⚠️ masqué par
     `body.no-burger` (owner 2026-07-26, sitter 2026-07-30) : le dashboard copie
     l'appli, qui n'a pas de tiroir ; seul le dock du bas navigue. Cf. la règle
     `.no-burger` dans le bloc SHELL MOBILE plus bas. Le hamburger n'est plus
     monté du tout (shared.js).
     ⚠️ Le format appli vaut ≤1024px POUR LES DEUX RÔLES — propriétaire depuis
     le 2026-08-24, sitter depuis le 2026-08-26 (demande Julie) — cf.
     nekoShellModeSync (shared.js). Le seuil par rôle est ABROGÉ : ne pas
     restaurer « sitter ≤767px » en relisant un texte de ces deux jours-là.
   - Desktop (sidebar fixe à gauche) : les deux rôles, dès 1025px.
   Le layout (photo + nom, items icône+label, pied communauté) est commun ;
   seul l'affichage (tiroir escamotable vs sidebar fixe) change au breakpoint.
   ========================================================================= */
.neko-tabbar{
  position: fixed; top: 0; bottom: 0; left: 0;
  width: min(90%, 440px);
  z-index: 200;
  display: flex; flex-direction: column; align-items: stretch;
  gap: 0;
  padding: 24px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  /* Beige CLAIR, ni blanc ni le beige soutenu d'avant (2026-07-31). Le menu se
     donne comme une surface à part du contenu, qui reste sur le crème de la page
     et porte des cartes blanches — mais `--cream-2` (#F5EBDF) faisait une
     colonne franchement plus foncée que la page, qui pesait à côté d'une mise en
     page devenue légère (marque, intertitres, filet d'accent).
     ⚠️ `--cream`, c'est-à-dire EXACTEMENT le beige du carnet de /mon-espace
     (`.carnet-records`, `.carnet-empty`, `.cg-mealcard`…) et du fond de page
     (2026-07-31, demande explicite). Le menu ne se détache donc plus par sa
     couleur mais par son filet de droite et par les cartes blanches du contenu,
     qui ressortent sur le même beige. Ne pas « rétablir un contraste » en le
     repassant à `--cream-2` : c'est la couleur que la demande écarte. */
  background: var(--cream);
  border-right: 1px solid var(--line);
  box-shadow: 0 20px 60px rgba(31, 26, 26, 0.28);
  transform: translateX(-100%);
  transition: transform .32s cubic-bezier(.4, 0, .2, 1);
  /* ⚠️ LE MENU NE DÉFILE PAS (2026-07-31, demande explicite). Il a porté
     `overflow-y:auto` jusqu'à cette date, au motif que sur un écran court
     « Déconnexion » et le pied devaient rester atteignables. Le motif était bon,
     la solution non : une colonne de navigation qui défile se lit comme un
     contenu, et on ne voit plus d'un coup d'œil ce que le menu propose.
     ⚠️ MAIS `overflow:hidden` SEUL COUPERAIT LE PIED — c'est-à-dire le nom du
     compte et « Déconnexion », qui sont en bas. Il va donc TOUJOURS avec la
     règle de hauteur juste en dessous, qui retire le pied « communauté »
     (décoratif) quand l'écran est court. Retirer l'une sans l'autre rend la
     déconnexion inatteignable sur un portable 13 pouces. */
  overflow: hidden;
  overscroll-behavior: contain;
}
/* ⚠️ ÉCRAN COURT : on ne sacrifie JAMAIS un chemin. Le nom du compte et
   « Déconnexion » n'ont pas d'autre porte une fois la barre du haut supprimée
   (`body.has-tabbar .nav{display:none}`), et c'est ce qui passe sous la coupe
   en premier quand la colonne déborde.
   ⚠️ Il y avait ici un cran de plus, qui sacrifiait le bloc DÉCORATIF (les trois
   chats de « Chinez, donnez ») : ce pied est supprimé depuis le 2026-08-27, il
   ne reste donc rien de dispensable à couper — cf. le § `is-short` plus bas.

   ⚠️ CES TROIS CRANS SONT POSÉS PAR MESURE, PLUS PAR SEUIL (2026-08-23).
   Ils ont vécu sur `@media (max-height: 760px)` du 2026-07-31 au 2026-08-23, et
   ce seuil est devenu FAUX sans que personne ne le touche : le menu
   propriétaire a gagné deux entrées et un groupe le 2026-08-22 (Parrainage /
   Actualités / Avis Google à la place de Messages), donc ~150px, pendant que le
   nombre, lui, restait calé sur le menu de juillet. Résultat constaté : sur un
   écran d'environ 765px de haut, le pied « communauté » restait affiché
   (l'écran n'était pas « court » au sens du seuil) et c'est le pied
   D'IDENTITÉ qui passait sous la coupe — « Déconnexion » inatteignable, le
   défaut exact que la règle existait pour éviter.
   ⚠️ Un seuil en pixels ne se relit pas quand on ajoute une entrée ; une
   mesure, si. C'est désormais `nekoFitSideMenu()` (shared.js) qui compare la
   hauteur du contenu à celle de la colonne et pose la classe qui va bien — donc
   NE PAS remettre de `@media (max-height: …)` ici, et ne pas se demander à
   quel nombre le recaler la prochaine fois qu'une entrée arrive.
   ⚠️ L'ordre des crans n'est pas arbitraire : on sacrifie d'abord le
   DÉCORATIF, ensuite l'ESPACE, et le défilement en dernier recours seulement —
   sur une fenêtre si basse que l'alternative serait une entrée hors d'atteinte.
   Le « menu qui ne défile pas » du 2026-07-31 tient donc partout où il peut
   tenir. */
/* ⚠️ LE CRAN 1 (`is-short`) N'EXISTE PLUS (2026-08-27) : il ne faisait qu'une
   chose, masquer le pied « communauté » sur un écran court, et ce pied est
   supprimé. Il ne reste donc que deux crans, et `nekoFitSideMenu` (shared.js) a
   perdu le sien en même temps — les deux vont ensemble, laisser la classe sans
   règle donnerait une mesure de plus pour rien.
   ⚠️ Le prochain qui aura besoin d'un cran 1 devra CHOISIR ce qu'il sacrifie :
   il n'y a plus de bloc décoratif dans cette colonne. Tout ce qui reste est un
   chemin (les entrées, l'identité) ou un réglage (les notifications), et le
   pavé ci-dessous dit pourquoi on ne coupe jamais un chemin. */
/* Cran 1 — ON RESSERRE, ON NE RETIRE RIEN. Toutes les entrées restent à
   l'écran, elles respirent moins : ~105px repris sur la marque, les deux CTA,
   la hauteur des lignes et l'espace des intertitres. */
.neko-tabbar.is-tight .neko-side-title{ padding-bottom: 12px; }
.neko-tabbar.is-tight .neko-side-brand{ padding-top: 8px; }
.neko-tabbar.is-tight .neko-side-brand-mark{ width: 44px; height: 44px; }
.neko-tabbar.is-tight .neko-side-shortcuts{ margin-bottom: 12px; }
.neko-tabbar.is-tight .neko-side-cta{ min-height: 44px; padding: 11px 12px; margin-bottom: 8px; }
.neko-tabbar.is-tight .neko-tab-list{ padding-top: 10px; }
.neko-tabbar.is-tight .neko-tab{ min-height: 44px; }
.neko-tabbar.is-tight .neko-tab + .neko-side-group{ padding-top: 12px; }
/* Cran 2 — DERNIER RECOURS (fenêtre plus basse que ~620px, soit un 800×600 ou
   un navigateur à fort zoom). Une colonne de navigation qui défile se lit comme
   un contenu, c'est le motif du 2026-07-31 et il tient toujours — mais une
   entrée qu'aucun geste n'atteint est pire. */
.neko-tabbar.is-scroll{ overflow-y: auto; }
.neko-tabbar.open{ transform: translateX(0); }

/* Le menu vertical (tiroir mobile / sidebar desktop) remplace la barre du haut :
   dès qu'il est monté (connecté), on supprime la .nav sur toutes les largeurs. */
body.has-tabbar .nav{ display: none; }

/* Voile derrière le tiroir mobile. */
.neko-tabbar-backdrop{
  position: fixed; inset: 0; z-index: 199;
  background: rgba(31, 26, 26, 0.42);
  opacity: 0; pointer-events: none;
  transition: opacity .3s ease;
}
.neko-tabbar-backdrop.open{ opacity: 1; pointer-events: auto; }

/* Bouton hamburger (haut-gauche) — ouvre le tiroir. Masqué en desktop.
   Look CTA hero : fill corail, liseré encre, tranche 3D coralInk (s'enfonce au clic).

   ⚠️ `absolute`, PAS `fixed` (2026-07-21) : ancré en haut du document, il part
   avec le scroll comme le reste de l'en-tête. En `fixed` il restait collé au
   coin de la fenêtre et venait se poser sur les cartes du dashboard dès qu'on
   descendait (« il descend quand on scroll »). Le bouton est le dernier enfant
   de <body>, non positionné → son bloc conteneur est le bloc initial, donc
   top/left le calent bien sur le coin haut-gauche de la PAGE. Le tiroir
   (.neko-tabbar) et son voile restent `fixed`, eux : un panneau coulissant doit
   couvrir la fenêtre. */
.neko-menu-btn{
  position: absolute; top: 12px; left: 12px; z-index: 120;
  width: 46px; height: 46px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--terracotta); color: #fff;
  border: 1.5px solid var(--ink);
  box-shadow: 0 4px 0 0 #C44C3D, 0 8px 18px -8px rgba(224, 74, 57, .55);
  cursor: pointer;
  transition: transform .12s ease, box-shadow .12s ease;
}
.neko-menu-btn svg{ width: 22px; height: 22px; }
/* ⚠️ Le hamburger passe TOUJOURS derrière un pop-up de réglages.
   Les panneaux .ap-detail (/profil-sitter, /profil-proprietaire) vivent dans
   `.wrap` — qui porte `z-index:1` et crée donc un contexte d'empilement. Leur
   `z-index:200` est prisonnier de ce contexte et ne peut pas dépasser le bouton,
   fixé sur le body à 120 : il ressortait par-dessus le panneau. On rabaisse donc
   le bouton pendant qu'un panneau est ouvert (classe posée par la page), au lieu
   de surenchérir sur le z-index du panneau, qui ne peut pas gagner. */
body.ap-modal-open .neko-menu-btn{ z-index: 0; }
.neko-menu-btn:hover{ transform: translateY(-1px); box-shadow: 0 5px 0 0 #C44C3D, 0 10px 22px -8px rgba(224, 74, 57, .6); }
.neko-menu-btn:active{ transform: translateY(4px); box-shadow: 0 0 0 0 #C44C3D, 0 2px 8px -4px rgba(224, 74, 57, .5); }
@media (prefers-reduced-motion: reduce){ .neko-menu-btn, .neko-menu-btn:hover, .neko-menu-btn:active{ transform: none; } }

/* Bouton messagerie flottant (haut-droite) — desktop uniquement. En mobile la
   messagerie est accessible via l'icône du header (à côté du « Bonjour »). */
/* Dock flottant (messagerie + notifications) — desktop only. Capsule arrondie
   à ombre douce ; les deux icônes sont plates à l'intérieur (plus de cercles
   séparés). Messagerie à gauche, notifications à droite.
   ⚠️ Ce dock ne s'affiche PAS sur les pages qui hébergent déjà ces deux actions
   dans leur propre mise en page : elles posent `body.has-rail-actions` et le
   masquent (cf. /mon-espace, où messagerie + notifs vivent en haut du rail de
   droite, au-dessus des CTA). Sans ça, les contrôles apparaissent en double et
   la capsule vient se poser sur le contenu de la colonne. */
.neko-fab-dock{ display: none; }
.neko-msg-btn, .neko-notif-btn{
  position: relative;
  width: 44px; height: 44px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--ink);
  border: 0; cursor: pointer;
  transition: color .15s ease, background .15s ease;
}
/* Desktop : deux boutons blancs libellés côte à côte (icône + texte), au dessin
   des CTA du site (2026-08-04) : petit radius (--radius), liseré encre remonté
   de 3px, tranche 3D dure dessous, typo Lora — la déclinaison claire de
   `.neko-side-cta.is-beige`. ⚠️ Un seul autre exemplaire recopié à la main,
   à tenir en miroir : le cluster #dash-actions de /mon-espace. Il y en avait un
   troisième, `.neko-owner-rail .rail-actions`, parti avec le rail cat-sitter le
   2026-08-23. */
@media (min-width: 768px){
  /* ⚠️ PAS en format appli (2026-08-24, LES DEUX RÔLES depuis le 2026-08-26) :
     en tablette comme sur téléphone, le dock du bas porte un onglet
     « Messages » qui rend cette capsule redondante — c'était déjà le motif de
     son absence sous 768px.
     ⚠️ « Le sitter tablette la garde », qui figurait ici du 24 au 26 août, est
     ABROGÉ : son shell est passé en format appli le 2026-08-26, il a donc le
     même onglet « Messages » que le propriétaire. Une page qui rallumait la
     capsule dans la bande 768–1024 (c'était le cas de /espace-home-sitting)
     poserait maintenant un second bouton au-dessus du dock.
     Conséquence à connaître : la capsule ne vit plus qu'À PARTIR DE 1025px, et
     seulement sur les pages qui ne montent pas la colonne messagerie. */
  body.neko-mobile-shell .neko-fab-dock{ display: none; }
  .neko-fab-dock{
    position: fixed; top: 16px; right: 22px; z-index: 120;
    display: flex; flex-direction: row; align-items: center; gap: 12px;
    padding: 0; border: 0; background: transparent; box-shadow: none;
  }
  .neko-fab-sep{ display: none; }
  .neko-msg-btn, .neko-notif-btn{
    width: auto; height: 44px; border-radius: var(--radius);
    gap: 10px; padding: 0 20px 0 17px;
    background: var(--white); border: 1px solid transparent;
    box-shadow: 0 1px 0 0 #1F1A1A;
    transition: color .14s ease, box-shadow .1s ease, transform .1s ease;
  }
  /* Liseré encre remonté de 3px — le « trait dessiné » des CTA. En ::before (le
     libellé occupe déjà ::after) et non en `border` : une bordure suivrait le
     bouton dans son enfoncement au clic, alors que le trait doit rester posé. */
  .neko-msg-btn::before, .neko-notif-btn::before{
    content: ""; position: absolute; inset: 0;
    border: 1px solid #1F1A1A; border-radius: inherit;
    transform: translateY(-3px); pointer-events: none;
  }
  .neko-msg-btn::after, .neko-notif-btn::after{
    font-family: var(--serif); font-size: 14.5px; font-weight: 400;
    color: inherit; white-space: nowrap; line-height: 1.2;
  }
  .neko-msg-btn::after{ content: "Messagerie"; }
  .neko-notif-btn::after{ content: "Notifications"; }
  .neko-msg-btn:hover, .neko-notif-btn:hover{
    color: var(--terracotta); background: var(--white);
    box-shadow: 0 2px 0 0 #1F1A1A;
    transform: translateY(-1px);
  }
  .neko-msg-btn:active, .neko-notif-btn:active{
    transform: translateY(1px); box-shadow: 0 0 0 0 #1F1A1A;
  }
  .neko-msg-btn svg, .neko-notif-btn svg{ width: 22px; height: 22px; }
  /* Pastille « non lu » ré-ancrée sur l'icône, pas au bout de la pilule. */
  .neko-msg-btn .neko-msg-btn-dot,
  .neko-notif-btn .neko-notif-btn-dot{ top: 10px; left: 31px; right: auto; }
}
@media (min-width: 768px) and (prefers-reduced-motion: reduce){
  .neko-msg-btn:hover, .neko-notif-btn:hover,
  .neko-msg-btn:active, .neko-notif-btn:active{ transform: none; }
}
.neko-msg-btn-dot, .neko-notif-btn-dot{
  position: absolute; top: 7px; right: 8px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--terracotta); border: 2px solid var(--white);
}

/* ===== INTERRUPTEURS DE NOTIFICATION DU CAT-SITTER (email / push app) =====
   Rendus par `nekoSitterAlertsMarkup()` (shared.js) et câblés par
   `nekoWireSitterAlerts()`. Seul porteur aujourd'hui : l'onglet « Paiement » de
   /moyens-de-paiement.
   ⚠️ CES RÈGLES VIVAIENT SOUS `.neko-owner-rail`, DANS LE BLOC @media 1025px
   (jusqu'au 2026-08-23, où le rail cat-sitter a été remplacé par la messagerie).
   Elles en sont sorties DEUX FOIS : plus de préfixe de colonne, et plus de
   media query — le bloc vit maintenant dans le flux d'une page, donc à toutes
   les largeurs. Ne pas les y remettre : sous 1025px les interrupteurs
   disparaîtraient sans que rien ne le dise, et un sitter au téléphone n'aurait
   plus aucun moyen de couper ses alertes.
   Les noms de classe sont neutres (`.neko-alert`, pas `.rail-alert`) : plus
   aucune colonne ne les porte. */
.neko-alerts{ display: flex; flex-direction: column; gap: 10px; }
.neko-alert{
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 14px; border-radius: 16px; cursor: pointer;
  background: var(--white); border: 1px solid var(--line);
  transition: border-color .2s ease;
}
.neko-alert:hover{ border-color: color-mix(in oklab, var(--ink) 18%, var(--line)); }
.neko-alert-txt{ min-width: 0; }
.neko-alert-txt b{
  display: block; font-family: var(--sans); font-size: 13.5px; font-weight: 600; color: var(--ink);
}
.neko-alert-txt small{
  display: block; margin-top: 2px; font-size: 11.5px; line-height: 1.35; color: var(--muted);
}
/* Interrupteur : la piste s'allume en terracotta, le pouce glisse. L'input
   reste focusable (opacity:0 au-dessus de la piste), pas de display:none. */
.neko-switch{ position: relative; flex: 0 0 auto; width: 40px; height: 24px; }
.neko-switch input{
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0;
  opacity: 0; cursor: pointer; z-index: 1;
}
.neko-switch i{
  position: absolute; inset: 0; border-radius: 999px;
  background: var(--muted); transition: background .18s ease;
}
.neko-switch i::after{
  content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(31, 26, 26, .25);
  transition: transform .18s ease;
}
.neko-switch input:checked + i{ background: var(--terracotta); }
.neko-switch input:checked + i::after{ transform: translateX(16px); }
.neko-switch input:focus-visible + i{ outline: 2px solid var(--terracotta); outline-offset: 2px; }

/* ============ RAIL OWNER — colonne de droite du dashboard propriétaire ============
   ⚠️ PORTÉE : uniquement les pages du dashboard owner, celles qui appellent
   neckoMountOwnerRail() (elles seules posent body.has-owner-rail). La landing et
   le reste du site ne sont pas concernés : sans cette classe, rien ci-dessous ne
   s'applique.
   Colonne FIXE (et non colonne de grille) pour être identique d'une page à
   l'autre sans toucher à la mise en page de chacune : seul un gouttière à droite
   est réservée sur le body. Desktop only — sous 1025px le rail disparaît et les
   actions repartent dans la nav (cf. neckoMountOwnerRail). */
.neko-owner-rail{ display: none; }
/* Le rail porte déjà la messagerie : la capsule flottante ferait doublon ET
   viendrait se poser sur le rail (tous deux ancrés en haut à droite).
   ⚠️ SEULEMENT À PARTIR DE 1025px depuis le 2026-08-22, et ce n'est pas un
   ajustement cosmétique. Cette règle valait à TOUTES les largeurs, au motif que
   « sous 1025px les actions repartent dans la nav ou le cluster d'en-tête » :
   vrai sur /mon-espace (cluster #dash-actions), FAUX partout ailleurs. Entre
   768 et 1024px le rail n'est pas encore monté, le dock du bas s'arrête à
   767px, et la barre du haut est supprimée par `body.has-tabbar
   .nav{display:none}` — la seule porte restante était l'entrée « Messages » du
   menu vertical, RETIRÉE côté propriétaire le même jour (NEKO_OWNER_TABS,
   shared.js). Les deux modifications vont ensemble : rendre cette règle à
   toutes les largeurs rendrait la messagerie INATTEIGNABLE sur ces écrans.
   ⚠️ CE RAISONNEMENT NE VAUT PLUS POUR PERSONNE, et la borne à 1025px n'a donc
   plus d'effet : le propriétaire est passé en format appli le 2026-08-24, le
   sitter le 2026-08-26, et leur dock du bas monte à 1024px en portant
   « Messages ». La capsule est déjà masquée sur toute cette plage par la règle
   `body.neko-mobile-shell .neko-fab-dock` du bloc .neko-fab-dock plus haut ;
   celle-ci ne fait plus que la même chose au-dessus de 1025px, là où la colonne
   messagerie est bien montée. La media reste, elle documente le seul cas
   qu'elle traite encore — ne pas conclure de son inutilité apparente qu'on peut
   la retirer, elle redeviendrait la seule protection le jour où un rôle
   repasserait à un seuil plus bas.
   ⚠️ /mon-espace n'est pas concernée : elle masque la capsule par
   `body.has-rail-actions`, à toutes les largeurs et pour la bonne raison (son
   en-tête porte le bouton dès 768px, le dock le porte en dessous). */
@media (min-width: 1025px){
  body.has-owner-rail .neko-fab-dock{ display: none; }
  /* 332 (largeur) + 16 (le décollement du bord droit) + 8 de respiration. */
  body.has-owner-rail{ padding-right: 356px; }
  /* ⚠️ PANNEAU FLOTTANT À COINS ARRONDIS (2026-07-31). La colonne était collée
     aux trois bords de la fenêtre, séparée du contenu par un simple filet
     gauche : quatre angles droits contre le verre, alors que tout ce qu'elle
     contient (onglets, cartes, CTA) est arrondi. On la décolle de 16px et on la
     referme sur un rayon de 22px.
     ⚠️ `overflow-y:auto` suffit à faire clipper le contenu par le rayon — ne pas
     ajouter `overflow:hidden` sur la base, ça supprimerait le défilement de la
     colonne sitter. `.is-msg` porte le sien parce que c'est LUI qui défile
     (`.rail-msg-host`), pas la colonne.
     ⚠️ Si la largeur change, reporter la somme sur `padding-right` ci-dessus :
     sans ça le contenu de la page passe SOUS le panneau. */
  /* ⚠️ 26px en haut, et non les 88px hérités (corrigé le 2026-08-09) : ce
     dégagement passait sous la barre de nav flottante, or les pages qui montent
     ce rail portent TOUTES la sidebar, qui supprime la nav
     (`body.has-tabbar .nav{display:none}`). Il ne dégageait donc rien et
     poussait « Mon compte » à un sixième de l'écran, sous un grand vide.
     La valeur s'aligne sur le `padding-top:26px` de la sidebar de gauche, pour
     que les deux colonnes démarrent à la même hauteur. C'est exactement la
     correction déjà faite pour `.is-msg` (rail propriétaire) : le rail sitter
     avait gardé l'ancienne valeur. */
  /* ⚠️ OMBRE DE LA COLONNE (2026-08-15, demande Julie). Le panneau est décollé
     des trois bords depuis le 2026-07-31 mais restait posé à plat : à 22px de
     rayon et sur un fond de la MÊME couleur que la page (`--cream` des deux
     côtés), seul un filet d'1px le détachait, et il se lisait comme une zone
     découpée plutôt que comme un panneau posé dessus.
     ⚠️ Ombre AMBIANTE, pas la tranche dure corail des cartes et des CTA
     (`3px 3px 0` sur `.hs-card`, `.rail-me-public`) : cette grammaire-là dit
     « bouton qu'on enfonce » et vaut pour un objet de la taille d'une main, pas
     pour une colonne haute de tout l'écran, où elle dessinerait une barre pleine
     sur deux côtés. Même famille que les ombres douces déjà présentes ici
     (`0 14px 30px -20px`, `0 20px 60px`), un cran au-dessus en portée puisque
     l'objet est plus grand.
     ⚠️ Deux couches, et les deux comptent : la large donne la hauteur, la courte
     recolle le bord au verre. Avec la seule large, le panneau flotte sans qu'on
     sache où il touche.
     ⚠️ Le filet reste : l'ombre le double, elle ne le remplace pas. Sur un écran
     à faible contraste, l'ombre disparaît la première. */
  .neko-owner-rail{
    display: block; position: fixed; top: 16px; right: 16px; bottom: 16px; width: 332px;
    z-index: 40; overflow-y: auto; overscroll-behavior: contain;
    padding: 26px 28px 32px 32px;
    background: var(--cream);
    border: 1px solid var(--line); border-radius: 22px;
    box-shadow: 0 24px 60px -28px rgba(31, 26, 26, .38), 0 3px 10px -6px rgba(31, 26, 26, .14);
  }
  /* ⚠️ TOUT LE CONTENU DU RAIL CAT-SITTER A ÉTÉ SUPPRIMÉ ICI (2026-08-23,
     demande Julie) : ~245 lignes qui habillaient la carte d'identité
     (`.rail-me*`), le CTA « Voir mon profil public » (`.rail-me-public`), la
     paire de boutons (`.rail-actions`), les intertitres et filets
     (`.rail-label`, `.rail-sep`), les interrupteurs de notification
     (`.rail-alert*`, `.rail-switch`) et les tuiles d'activité
     (`.neko-owner-rail .ss-stat*`). Le sitter reçoit désormais la colonne du
     propriétaire, c'est-à-dire la messagerie, juste en dessous.
     Où sont passés leurs contenus : voir le pavé de tête de la section RAIL
     LATÉRAL de `shared.js`, qui les liste un par un. Deux conséquences à
     connaître avant de recoller quoi que ce soit ici :
       · les interrupteurs ne sont PAS supprimés, ils sont remontés en haut de ce
         fichier (`.neko-alerts`), hors media query, parce qu'ils vivent
         désormais dans le flux d'une page ;
       · `.rail-cta` et `.rail-map*` sont partis avec, alors qu'ils étaient déjà
         morts avant (plus aucun rail ne rendait de raccourcis ni la carte
         « Autour de vous » depuis le 2026-07-29). `neckoRailMap()` et
         `neckoRailMapCount()` survivent dans shared.js, sans garde-corps CSS :
         les réveiller demande de réécrire cet habillage.
     ⚠️ `.neko-owner-rail{width:332px}` et `body.has-owner-rail{padding-right:356px}`
     ci-dessus ne sont plus qu'un REPLI d'un tick : toute colonne montée
     aujourd'hui est `.is-msg`, donc large de 28vw (`body.neko-msg-pinned`, plus
     bas). Ne pas les lire comme la largeur réelle de la colonne. */


  /* ===== COLONNE MESSAGERIE (les deux rôles) ===================================
     Le rail owner n'affiche plus de raccourcis : il héberge la messagerie, qui
     reste ouverte en permanence (2026-07-29) — et depuis le 2026-08-23 c'est
     AUSSI la colonne du cat-sitter, dont le rail de raccourcis a été supprimé. Le panneau déplacé ici est le VRAI
     tiroir (#neko-msg-drawer) — d'où les remises à plat ci-dessous, qui défont
     son habillage de panneau coulissant : position fixe, largeur imposée, ombre
     portée, voile. Sans elles il se superposerait à la page au lieu de s'y
     encastrer.
     ⚠️ `overflow: hidden` sur la colonne : c'est la LISTE qui défile à
     l'intérieur (.neko-drawer-body), pas la colonne. Deux barres de défilement
     imbriquées rendraient le fil de discussion impossible à suivre. */
  /* ⚠️ 26px de haut, et non les 104px du rail classique : celui-ci devait passer
     sous la barre de nav flottante, or sur ces pages la nav est masquée au profit
     de la sidebar (`body.has-tabbar .nav{display:none}`). Les 96px hérités ne
     dégageaient donc rien et poussaient les deux onglets au tiers de l'écran.
     La valeur s'aligne sur le `padding-top:26px` de la sidebar de gauche, pour
     que les deux colonnes démarrent à la même hauteur. */
  /* ⚠️ BLANC À L'ESSAI (2026-08-24, demande Julie « pour voir », une heure
     après avoir demandé un beige plus soutenu — le beige essayé était
     `color-mix(in oklab, var(--cream) 55%, var(--cream-2))`, à restaurer ici
     si le blanc ne convainc pas). Historique utile : la colonne a DÉJÀ été en
     `--cream-2` plein, abandonné le 2026-08-14 parce qu'il fallait REPEINDRE
     tout l'intérieur (fil blanc, bulles beiges) ; désormais on ne repeint
     RIEN — le fil et la barre de saisie sont transparents sous la colonne
     (deux règles plus bas), donc UNE seule surface porte la couleur et elle se
     règle sur cette seule ligne. */
  .neko-owner-rail.is-msg{
    display: flex; flex-direction: column; overflow: hidden;
    padding: 26px 0 0; background: var(--white);
  }
  /* ⚠️ Complément indispensable de la couleur ci-dessus : `.neko-msg-thread` et
     `.neko-msg-compose` portent `background:var(--cream)` pour le TIROIR
     coulissant — invisible tant que la colonne était `--cream` aussi, mais un
     rectangle plus clair dès qu'elle s'assombrit. Transparents ICI seulement :
     le tiroir garde ses fonds. Ce n'est PAS le retour des « repeints » que le
     bloc SURFACE UNIE interdit — on ne pose pas une couleur, on en retire une. */
  body.neko-msg-pinned .rail-msg-host .neko-msg-thread{ background: transparent; }
  body.neko-msg-pinned .rail-msg-host .neko-msg-compose{ background: transparent; }
  /* ⚠️ LES DEUX ONGLETS « Messagerie / Notifs » ONT ÉTÉ SUPPRIMÉS LE 2026-08-13,
     avec le tiroir de notifications (cf. `public/pages/shared.js` § DRAWER).
     Sont parties avec eux les règles `.rail-msg-head`, `.rail-msg-tab` (+ son
     `::before`, ses états et `.is-on`) et `.rail-msg-dot` : la colonne n'a plus
     qu'un panneau, et un jeu d'onglets à un seul onglet ne choisit rien.
     La colonne monte donc directement `.rail-msg-host`, qui récupère les ~54px
     que la barre occupait. */
  /* Corps SANS FOND (2026-07-29) : la liste se pose à même la colonne beige. La
     carte blanche essayée juste avant dessinait un grand rectangle vide dès qu'il
     y avait deux conversations — le fond signalait une surface pleine là où il
     n'y avait rien. Sans elle, la colonne se termine avec la dernière ligne.
     ⚠️ PLUS AUCUNE MARGE LATÉRALE (2026-08-24, demande Julie « full width ») :
     les 14px de chaque côté s'additionnaient aux 14px du corps et aux 18px de
     la ligne, soit 46px d'encaissement par côté — dans une colonne d'un quart
     de page, un tiers de la largeur partait en marges. Le contenu va désormais
     au bord : ce sont les paddings PROPRES de chaque bloc (en-tête 18px, ligne
     de conversation 18px, fil 18px, barre de saisie 16px) qui posent le seul
     retrait. Ne pas remettre de marge ici « pour aérer » : c'est le padding du
     bloc concerné qu'on ajuste. */
  .neko-owner-rail.is-msg .rail-msg-host{
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    margin: 0; background: transparent;
  }
  /* ⚠️ `.rail-msg-title` EST PARTIE AVEC L'INTITULÉ « Messagerie » (2026-08-23,
     demande Julie, cf. `neckoMsgRailMarkup` dans `shared.js`). Elle alignait ce
     titre sur le contenu de l'en-tête du panneau (32px = les 14 de l'hôte + les
     18 de l'en-tête) et l'empêchait de se comprimer dans la colonne flex. Plus
     aucun nœud ne la porte : la colonne monte directement `.rail-msg-host`. */
  /* ═══ SURFACE UNIE — la colonne ne se repeint PLUS (2026-08-14, demande Julie).
     Sept règles vivaient ici, toutes filles du même choix : la colonne étant en
     `--cream-2` et le fil du tiroir en `--cream`, il fallait INVERSER tout
     l'intérieur pour qu'il reste lisible — fil blanc, bulles beiges sans filet,
     encadré « rencontre » beige, barre de saisie blanche, survol des lignes en
     blanc. Le résultat était un grand rectangle blanc encastré dans le beige,
     décollé de 14px de chaque bord par le `padding` du corps : une délimitation
     que rien ne justifiait, au milieu d'un panneau qui n'a qu'une surface.
     La colonne repasse en `--cream`, donc les repeints n'ont plus d'objet : le
     fil, la barre de saisie et le bandeau de choix retrouvent leur `--cream`
     d'origine et se fondent dans la colonne, les bulles et l'encadré retrouvent
     leur blanc + filet, et le survol des lignes son `--cream-2`, qui se voit à
     nouveau. La colonne est désormais peinte comme le tiroir coulissant, ce
     qu'elle EST — le même nœud, déplacé.
     ⚠️ NE PAS réintroduire un fond sur `.neko-msg-thread`, `.neko-msg-compose`
     ou `.neko-meet-banner` sous `body.neko-msg-pinned` : c'est exactement ce
     qui vient d'être retiré, et le premier de ces trois est celui qui dessinait
     le rectangle. Si la couleur de la colonne rechange un jour, c'est ELLE
     qu'on ajuste, pas son contenu. */
  /* Le tiroir déposé dans la colonne perd son habillage de panneau coulissant :
     sans ça il se superposerait à la page au lieu de s'y encastrer. Le fond
     reste transparent, c'est la colonne qui le porte.
     ⚠️ Le sélecteur reste `> .neko-drawer` et non `> #neko-msg-drawer` : c'est
     la classe que le tiroir messagerie porte (`.neko-drawer.neko-msg-drawer`),
     et elle ne désigne plus qu'à lui depuis que le tiroir de notifications a été
     supprimé (2026-08-13). */
  body.neko-msg-pinned .rail-msg-host > .neko-drawer{
    position: static; z-index: auto;
    width: auto; max-width: none; height: auto; min-height: 0;
    flex: 1 1 auto; transform: none; transition: none;
    background: transparent; border-left: 0; box-shadow: none;
  }
  /* Rien à fermer : le panneau est un morceau de page, pas un popup.
     ⚠️ ET SEULEMENT EN VUE LISTE (`:not(.neko-msg-thread)`), corrigé le
     2026-08-15. Dans un fil, la flèche « Retour », l'appel vidéo et le menu
     « Plus d'options » portent la MÊME classe `.neko-drawer-close` : sans cette
     restriction la règle les effaçait tous les trois, et une conversation
     ouverte dans la colonne n'avait plus AUCUNE sortie — on ne pouvait ni
     revenir à la liste, ni appeler, ni signaler. L'encart « Proposez une visio »
     renvoyait même à « le bouton d'appel, en haut », qui n'était pas là.
     ⚠️ C'est mot pour mot le piège déjà tombé sur le format mobile
     (`body.has-dock:not(.neko-msg-thread)`, plus bas), et pour la même raison :
     ces quatre boutons partagent une classe qui ne dit pas ce qu'ils font. Toute
     règle qui vise `.neko-drawer-close` doit se demander d'abord si elle parle
     de la vue LISTE ou du FIL. */
  body.neko-msg-pinned:not(.neko-msg-thread) .rail-msg-host > .neko-drawer .neko-drawer-close{ display: none; }
  /* Le voile du tiroir. Il vit dans son root d'origine, hors de la colonne :
     c'est donc le seul sélecteur qui ne passe pas par elle. */
  body.neko-msg-pinned #neko-msg-backdrop{ display: none; }
  body.neko-msg-pinned .rail-msg-host > .neko-drawer .neko-drawer-head{ padding: 16px 18px 12px; }
  /* ⚠️ BARRE DE DÉFILEMENT MASQUÉE (2026-08-15, demande Julie). Le contenu
     DÉFILE TOUJOURS — on retire la barre, pas le défilement : `overflow-y:auto`
     reste sur `.neko-drawer-body`, ainsi que son `overscroll-behavior:contain`
     (règle transversale, un panneau arrivé en bout de course ne repousse pas la
     page). Trois déclarations pour trois moteurs, aucune n'étant standard
     partout : `scrollbar-width` (Firefox), `-ms-overflow-style` (ancien Edge),
     `::-webkit-scrollbar` (Chrome, Safari, Edge actuel).
     ⚠️ Portée `body.neko-msg-pinned` : le TIROIR coulissant garde la sienne. Il
     recouvre la page et se lit seul, sa barre est le seul indice qu'il reste du
     texte dessous ; la colonne, elle, est posée à côté d'un contenu qu'on fait
     déjà défiler, et deux barres côte à côte se lisaient comme un cadre.
     ⚠️ Coût assumé : plus rien ne SIGNALE qu'une conversation continue au-delà
     du bord. Ce qui le compense est que le fil s'ouvre en bas, sur le dernier
     message — si un jour il s'ouvrait en haut, ce masquage serait à revoir. */
  /* ⚠️ PADDING LATÉRAL À ZÉRO (2026-08-24, demande Julie « full width », avec le
     retrait des marges de `.rail-msg-host` plus haut) : la liste et le fil vont
     au bord de la colonne, leur retrait de lecture étant porté par leurs propres
     paddings (18px la ligne de conversation, 18px le fil). Le survol d'une ligne
     et ses filets courent ainsi d'un bord à l'autre, comme une vraie liste. */
  body.neko-msg-pinned .rail-msg-host > .neko-drawer .neko-drawer-body{
    padding: 14px 0 20px;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  body.neko-msg-pinned .rail-msg-host > .neko-drawer .neko-drawer-body::-webkit-scrollbar{
    width: 0; height: 0; display: none;
  }
  /* ═══ EN-TÊTE DE LA VUE LISTE — « Messages » + pillule Berlioz (2026-08-15,
     demande Julie), c'est-à-dire EXACTEMENT le format mobile.
     ⚠️ L'en-tête était MASQUÉ ici jusqu'à cette date, au motif que « la colonne
     EST la messagerie, un titre pour un panneau unique ne distingue rien ».
     C'était vrai quand la colonne portait deux onglets nommés au-dessus d'elle ;
     ils sont partis le 2026-08-13 et plus rien ne disait ce qu'on regardait —
     la colonne s'ouvrait sur une liste de noms sans en-tête, au bord d'une page
     qui parle d'autre chose. Ne pas re-masquer.
     ⚠️ Le sur-titre « Messagerie » n'est plus masqué ICI : il a été SUPPRIMÉ DU
     DOM le 2026-08-15 (cf. `nekoRenderMsgList` dans shared.js). Il doublait le
     titre à 30px de distance, et le masquer par CSS le laissait réapparaître
     dans le tiroir coulissant entre 768 et 1024px, où aucune règle ne le visait.
     Ne pas réécrire de `display:none` pour lui : il n'y a plus rien à masquer. */
  body.neko-msg-pinned:not(.neko-msg-thread) #neko-msg-drawer > .neko-drawer-head{
    display: flex; border-bottom: 0;
  }
  /* 22px et non les 26px du mobile : là-bas le titre ouvre un écran entier.
     ⚠️ Le `display` n'est plus ici : le titre est visible par défaut depuis le
     2026-08-15, ce bloc ne règle plus que sa taille. */
  body.neko-msg-pinned .neko-msg-listtitle b{ font-size: 22px; }
  body.neko-msg-pinned .neko-msg-botpill{ display: inline-flex; }
  /* ⚠️ La grande carte « Parlez à Berlioz » du pied est MASQUÉE : la pillule de
     l'en-tête dit la même chose, en un quart de la place. Même arbitrage qu'en
     mobile (`#neko-msg-cta{display:none}` sous 767px), et pour la même raison.
     ⚠️ Elle n'est plus DÉPLACÉE dans la colonne non plus : `nekoPinMessagerie`
     a cessé de le faire le 2026-08-15, `nekoRailSyncCta` est partie avec, et la
     règle `.neko-owner-rail.is-msg > .neko-msg-cta` qui l'habillait en pied de
     colonne n'avait plus d'objet. Le sélecteur ci-dessous vise donc la carte
     restée DANS le tiroir. */
  body.neko-msg-pinned .neko-msg-cta{ display: none; }
  .neko-owner-rail.is-msg #neko-msg-drawer{ display: flex; }
  /* ⚠️ `.neko-owner-rail.is-msg > .neko-msg-cta` A ÉTÉ SUPPRIMÉE (2026-08-15).
     Elle habillait « Parlez à Berlioz » en pied de colonne, du temps où
     `nekoPinMessagerie` l'y déplaçait. La pillule « Berlioz · IA » de l'en-tête
     l'ayant remplacé, la carte ne sort plus du tiroir et ce sélecteur ne
     désignait plus rien. */
  /* ═══ LARGEUR DE LA COLONNE MESSAGERIE — fluide, et plus large que le rail
     de raccourcis (2026-08-14, demande Julie sur /espace-home-sitting).
     332px, c'est la largeur d'une colonne qu'on CONSULTE. Quand on y écrit —
     et le home sitter n'a pas d'autre endroit pour le faire — le fil se réduit
     à quatre mots par ligne, l'encadré « rencontre » descend sur huit lignes,
     et lire un message demande de faire défiler. On l'élargit donc, mais pas
     d'un nombre fixe : à 1025px exactement il ne reste que ~430px de contenu,
     un élargissement en dur les mangerait. D'où une largeur EXPRIMÉE EN PART DE
     FENÊTRE : le quart de la page (`25vw`, 2026-08-23, demande Julie), monté à
     `28vw` le 2026-08-24 (« élargis encore un peu la messagerie », demande
     Julie, dans la foulée du passage de la liste en pleine largeur).
     ⚠️ NI PLANCHER NI PLAFOND, et c'est délibéré. Le réglage précédent était un
     `clamp(360px, 34vw, 500px)` : ses deux bornes faisaient mentir la part
     annoncée partout ailleurs qu'entre ~1060 et ~1470px de fenêtre — sous
     1060px la colonne restait clouée à 360px (soit 35 % à 1025px), au-dessus de
     1470px elle plafonnait à 500px (soit 20 % sur un 2560px). « Une part fixe
     de la page » est une consigne de mise en page, pas une fourchette : elle se
     tient à TOUTES les largeurs où cette colonne existe, c'est-à-dire ≥1025px.
     ⚠️ CE QUE ÇA COÛTE, ET C'EST ASSUMÉ : à 1025px la colonne tombe à 287px,
     et c'est précisément l'étroitesse que le `clamp` évitait — le fil s'y écrit
     à quelques mots par ligne. Le curseur à bouger si le réglage se rediscute
     est CELUI-CI, jamais les 332px de `.neko-owner-rail`, qui ne sont plus
     qu'un repli d'un tick depuis la suppression du rail cat-sitter
     (2026-08-23).
     ⚠️ `vw` ET NON `%`, et ce n'est pas cosmétique : la même valeur sert ici et
     dans le `padding-right` du body juste en dessous. En pourcentage, les deux
     ne se résoudraient PAS sur la même référence (le bloc conteneur d'un
     élément `position:fixed` d'un côté, la boîte de `html` de l'autre) : elles
     dériveraient l'une de l'autre et le contenu de la page repasserait sous la
     colonne. `vw` donne le même nombre aux deux. Contrepartie mineure : `vw`
     compte la barre de défilement classique, donc la colonne fait un cheveu de
     plus que le quart de la surface visible.
     ⚠️ Valeur RÉGLÉE À L'ŒIL jusqu'ici, six passes depuis le 2026-08-14 :
     33vw/460px, 27vw/400px, 30vw/430px, clamp(360px,34vw,500px), 25vw, et
     enfin ces 28vw. Le va-et-vient n'est pas de l'indécision : entre la première et
     la dernière, le panneau a perdu la grande carte « Parlez à Berlioz » de son
     pied et gagné un en-tête titré, et ce qu'une largeur donne à lire a changé
     avec.
     ⚠️ LA SOMME EST TENUE PAR UNE VARIABLE, pas recopiée : le `padding-right`
     du body et la largeur de la colonne DOIVENT bouger ensemble, sinon le
     contenu de la page passe dessous (le rail est en `position:fixed`). Ne
     jamais réécrire l'une des deux valeurs en dur.
     ⚠️ Le sélecteur est `body.neko-msg-pinned` et pas `body.has-owner-rail` :
     ce dernier porte AUSSI le rail cat-sitter (332px, réglé pour ce qu'il
     contient), qui n'a rien demandé. Il est posé par `nekoPinMessagerie`, donc
     seulement quand cette colonne-ci est à l'écran, donc jamais sous 1025px.
     ⚠️ Déclaré APRÈS `body.has-owner-rail{padding-right:356px}` : même
     spécificité, c'est l'ordre qui tranche.
     ⚠️ CE BLOC A ÉTÉ MORT, et rien ne l'a signalé (réparé le 2026-08-23). Une
     fermeture de commentaire EN TROP refermait le bloc ci-dessus une trentaine
     de lignes trop tôt : tout le texte qui suivait était alors lu comme un
     SÉLECTEUR, `body.neko-msg-pinned` s'y raccrochait, et le navigateur jetait
     la règle entière. La colonne retombait donc sur le repli de la ligne
     d'après (360px à l'époque) pendant que le body gardait le
     `padding-right:356px` du rail cat-sitter : 384px attendus, 356px réservés,
     28px de contenu qui passent SOUS la colonne. C'est ce chevauchement qui a
     fait rouvrir le dossier, pas la largeur.
     ⚠️ Un commentaire CSS mal refermé ne lève aucune erreur, n'apparaît dans
     aucun test et se lit parfaitement dans l'éditeur — la seule vérification
     est mécanique : autant d'ouvertures que de fermetures.
     ⚠️ ET NE JAMAIS ÉCRIRE DE FERMETURE DE COMMENTAIRE EN PROSE, même entre
     accents graves pour la citer : le parseur ne lit pas les guillemets, il
     referme. Cette phrase-ci a reproduit le défaut qu'elle décrivait, à
     l'écriture même de la ligne du dessus. */
  body.neko-msg-pinned{
    --neko-msg-rail-w: 28vw;                              /* un bon quart de page */
    padding-right: calc(var(--neko-msg-rail-w) + 24px);   /* 16 de décollement + 8 de respiration */
  }
  /* Repli : la variable vit sur `body.neko-msg-pinned`, posé par
     `nekoPinMessagerie` juste APRÈS l'insertion de la colonne. Sans repli, cet
     intervalle d'un tick rendrait le `var()` invalide, donc `width:auto` sur un
     élément en `position:fixed` — une colonne à la largeur de son contenu.
     ⚠️ Le repli répète la MÊME valeur que la variable (il valait 360px tant que
     celle-ci était un `clamp`) : le tick d'attente ne doit pas être une largeur
     différente qui se corrige sous les yeux. Les deux bougent ensemble. */
  .neko-owner-rail.is-msg{ width: var(--neko-msg-rail-w, 28vw); }
  /* ═══ TEXTE DE LA COLONNE — un cran sous le tiroir coulissant.
     Le tiroir occupe 480px et se lit seul, écran gelé derrière ; la colonne
     est posée à côté d'une page qu'on continue de lire, et ses corps de texte
     y pesaient autant que les titres de la page. Un cran, pas deux : en
     dessous de ces valeurs le fil cesse d'être confortable, et c'est là qu'on
     travaille. ⚠️ Portée `body.neko-msg-pinned` : le tiroir coulissant garde
     ses tailles, il n'a pas le même voisinage. */
  body.neko-msg-pinned .neko-msg-peer b{ font-size: 14.5px; }
  body.neko-msg-pinned .neko-msg-peer small{ font-size: 11.5px; }
  /* ⚠️ ENCADRÉ « RENCONTRE » ÉLARGI (2026-08-15, demande Julie) : il porte
     `margin:16px 18px 0` par défaut, hérité du TIROIR de 480px. Dans la colonne
     ces 18px de chaque côté sont pris sur un texte qui n'en a déjà pas beaucoup,
     et l'encadré descendait sur six lignes. On les ramène à 4px et on resserre
     le rembourrage d'un point : ~30px de largeur de texte gagnés, deux lignes en
     moins. La marge HAUTE ne bouge pas, elle décolle l'encadré de l'en-tête.
     ⚠️ Assumé : l'encadré est alors un peu plus large que les bulles du fil
     (`.neko-msg-thread` garde ses 18px). C'est voulu — il coiffe la
     conversation, il ne s'aligne pas dessus. Ne pas « corriger » l'un par
     l'autre : rétrécir l'encadré annule la demande, élargir les bulles colle le
     texte des messages au bord de la colonne. */
  body.neko-msg-pinned .neko-meet-banner{ margin-left: 4px; margin-right: 4px; padding: 10px; gap: 8px; }
  body.neko-msg-pinned .neko-meet-banner .nmb-ic{ width: 30px; height: 30px; border-radius: 9px; }
  body.neko-msg-pinned .neko-meet-banner b{ font-size: 13px; }
  body.neko-msg-pinned .neko-meet-banner small{ font-size: 11.5px; }
  body.neko-msg-pinned .neko-msg-thread .msg{ font-size: 13px; }
  body.neko-msg-pinned .neko-msg-thread .msg.auto{ font-size: 12.5px; }
  /* ⚠️ UN DEMI-CRAN SOUS LE TIROIR, TOUJOURS — c'est le RAPPORT qui est la
     règle, pas les valeurs. Elles ont monté le 2026-08-23 (12.5/13.5 était le
     seul endroit du bureau où la messagerie se lisait plus petit qu'au
     téléphone), le nom seul est redescendu le 24 (deux passes : 17 → 15.5, puis
     14.5), et les DEUX sont redescendus le 2026-08-30 avec le tiroir
     (« le texte est trop gros, réduis ») : 14.5 → 13 et 15.5 → 13.5.
     ⚠️ Ne jamais toucher cette paire seule : restée à 14.5/15.5 pendant que le
     tiroir descendait à 13.5/14, la colonne serait devenue PLUS GROSSE que lui,
     c'est-à-dire l'inverse de ce que ce pavé affirme depuis le début. Le pavé
     jumeau vit au-dessus de `@media (min-width: 768px)`. */
  /* ⚠️ 14.5/15 depuis le 2026-08-31, montées AVEC le tiroir (15/15.5) : le
     demi-cran d'écart est conservé, c'est lui la règle. */
  body.neko-msg-pinned .conv-item b{ font-size: 14.5px; }
  body.neko-msg-pinned .conv-item .preview{ font-size: 15px; }
  body.neko-msg-pinned .neko-msg-compose input{ font-size: 13.5px; }
}
/* Marqueur MapLibre du rail (hors media query : réutilisable ailleurs). */
.neko-rail-marker{ display: block; color: var(--terracotta); line-height: 0; filter: drop-shadow(0 2px 4px rgba(31,26,26,.28)); }

/* --- En-tête profil : photo (du chat) au-dessus du nom. Rôle « Propriétaire »
   retiré ; taille compacte pour éviter le scroll du tiroir. --- */
.neko-side-only{ display: block; }
/* ⚠️ `.neko-side-public` a été SUPPRIMÉE le 2026-07-30. C'était le CTA « Voir mon
   profil public » du menu cat-sitter, avec son dessin à lui (pilule sans-serif de
   14px, icône œil, tranche de 3px, bordure encre) là où le menu propriétaire
   porte au même endroit les `.neko-side-cta` du hero. Deux rôles, un seul menu :
   ce CTA passe désormais par `.neko-side-shortcuts` comme ceux du propriétaire.
   Ne pas recréer une classe de bouton par rôle. */

/* --- Raccourcis PROPRIÉTAIRE dans le menu vertical (2026-07-29) ---------------
   « Publier mon annonce » / « Mes anciens sitters », descendus du rail de droite
   (qui porte désormais la messagerie). Même dessin que les anciens .rail-cta :
   liseré encre décalé de 3px + tranche 3D dure, la signature des CTA du site.
   ⚠️ Le liseré est dessiné en ::before et non en `border` : une bordure suivrait
   le bouton dans son enfoncement au clic, alors que le trait doit rester posé. */
/* ⚠️ Pas d'intertitre « RACCOURCIS » ici (retiré le 2026-07-29, demande
   explicite) : les deux boutons se lisent seuls, le mot ne disait rien de plus
   que ce qu'ils montrent. Ne pas le réintroduire par symétrie avec le rail
   sitter, qui garde les siens parce que ses blocs, eux, ne se devinent pas.

   ⚠️ DESSIN = celui du CTA DU HERO, c'est-à-dire `.hero .search-card.hero-cta-form
   .search-submit` (globals.css), et NON la règle de base `.search-submit` — cette
   dernière décrit un aplat encre à coins 6px qui n'atteint jamais l'écran, six
   blocs d'override plus bas la repeignant en pilule corail. Recette exacte :
   fond `--terracotta`, texte blanc, radius 26px, liseré encre de 1px dessiné en
   ::before et remonté de 3px, tranche 3D #C44C3D dessous, typo Lora 400 à 16px,
   56px de haut ; au survol la tranche passe à 2px et le bouton monte d'1px, au
   clic il s'enfonce. Les DEUX raccourcis le portent (demande du 2026-07-29).
   ⚠️ Aucune variable partagée entre les deux fichiers (la landing est un bundle
   Vite à part) : si le hero change, ça se répercute ici à la main. */
/* ⚠️ Le filet est PASSÉ SOUS les raccourcis (2026-07-31) : il les surmontait,
   c'est-à-dire qu'il séparait la marque des boutons plutôt que les boutons de la
   navigation. Il vit maintenant sur `.neko-tab-list` (sa règle de base), juste
   au-dessus du premier intertitre de groupe. */
.neko-side-shortcuts{
  width: 100%; margin: 0 0 16px; padding-top: 0;
  border-top: 0;
}
/* ⚠️ Padding horizontal RÉDUIT (14px au lieu des 32px du hero) : le bouton est en
   pleine largeur d'une colonne de 200px utiles, donc ce padding ne se voit pas —
   le texte est centré — mais avec 32px de chaque côté et `nowrap`, « Publier mon
   annonce » débordait. Tout le reste est celui du hero, au pixel. */
.neko-side-cta{
  position: relative;
  display: flex; align-items: center; justify-content: center;
  /* ⚠️ 50px de haut (2026-08-25, « agrandis les CTA »), entre les 40px du
     2026-07-29 et les 56 du hero : deux boutons empilés dans une colonne de
     200px au-dessus d'un menu ne doivent pas peser plus lourd que toute la
     navigation qu'ils surmontent, mais à 40px ils se lisaient comme une ligne
     de menu de plus. Le DESSIN ne change pas.
     ⚠️ ANGLES RESSERRÉS le même jour (« réduis les radius ») : 14px au lieu des
     22px qui, sur 40px de haut, en faisaient une pilule. Ne pas restaurer 22px
     en relisant « au pixel du hero » dans le pavé du dessus : le hero garde
     ses 26px, la colonne non. */
  width: 100%; min-height: 50px; text-align: center;
  padding: 14px 12px; margin-bottom: 10px;
  border: 1px solid transparent; border-radius: 14px;
  background: var(--terracotta); color: #fff;
  box-shadow: 0 1px 0 0 #C44C3D;
  font-family: var(--serif); font-weight: 400; font-size: 14px; line-height: 1.2;
  white-space: nowrap; cursor: pointer; text-decoration: none;
  transition: transform .1s ease, box-shadow .1s ease, background .14s ease, color .14s ease;
  will-change: transform;
}
.neko-side-cta:last-child{ margin-bottom: 0; }
/* Liseré encre remonté de 3px — le « trait dessiné » du hero. En ::before et non
   en `border` : une bordure suivrait le bouton dans son enfoncement au clic,
   alors que le trait doit rester posé. */
.neko-side-cta::before{
  content: ""; position: absolute; inset: 0;
  border: 1px solid #1F1A1A; border-radius: inherit;
  transform: translateY(-3px); pointer-events: none;
}
.neko-side-cta:hover{ transform: translateY(-1px); box-shadow: 0 2px 0 0 #C44C3D; }
.neko-side-cta:active{ transform: translateY(1px); box-shadow: 0 0 0 0 #C44C3D; }
/* Second raccourci : MÊME bouton, en clair sur texte encre. Sa tranche 3D est
   encre et non coralInk — une tranche corail sous un aplat clair se lisait comme
   un débordement du bouton du dessus.
   ⚠️ BLANC depuis que le menu est beige (2026-07-30) : il était en var(--cream-2),
   soit exactement la couleur que le tiroir porte maintenant — le bouton
   disparaissait dans son fond, réduit à son liseré. */
.neko-side-cta.is-beige{ background: var(--white); color: var(--ink); box-shadow: 0 1px 0 0 #1F1A1A; }
.neko-side-cta.is-beige:hover{ box-shadow: 0 2px 0 0 #1F1A1A; }
.neko-side-cta.is-beige:active{ box-shadow: 0 0 0 0 #1F1A1A; }
@media (prefers-reduced-motion: reduce){
  .neko-side-cta{ transition: background .14s ease, color .14s ease; }
  .neko-side-cta:hover, .neko-side-cta:active{ transform: none; }
}

/* ══ HABILLAGE GÉNÉRIQUE D'UNE PAGE EMBARQUÉE (2026-08-27). Toute page servie
   avec `?embed=…` (cf. `nekoEmbedBootstrap`, shared.js) perd ce que l'hôte porte
   déjà — sa navigation, son pied, ses gouttières — et son ascenseur, pour qu'il
   n'y en ait qu'UN sur l'écran, celui de l'hôte, qui suit la hauteur remontée
   par `neko-profil-height`.
   ⚠️ Les pages qui avaient écrit ces règles chez elles (/espace-sitter,
   /moyens-de-paiement, /profil-sitter) les gardent : elles en ont de plus
   précises, et ces trois-là ne sont que le socle. Rien à retirer là-bas.
   ⚠️ Posé sur `html` ET `body` pour l'ascenseur : la barre d'une page est celle
   de la VUE, que le navigateur prend sur `html` et non sur `body`. */
html.embed, body.embed{ scrollbar-width:none; }
html.embed::-webkit-scrollbar, body.embed::-webkit-scrollbar{ width:0; height:0; }
body.embed #nav-slot, body.embed .foot, body.embed .neko-menu-btn{ display:none; }
body.embed .wrap{ padding-left:0; padding-right:0; max-width:none; }
/* ⚠️ `.wrap-narrow` AUSSI (2026-08-30) : /supprimer-mon-compte et
   /appareils-connectes s'ouvrent en panneau dans « Mon compte », et elles
   portent celle-ci, pas `.wrap`. Sans la règle, la page embarquée gardait ses
   28px de gouttière ET son plafond de 640px à l'intérieur d'un cadre qui les
   pose déjà — deux marges empilées, un contenu qui flotte au milieu.
   ⚠️ Et `.page-head` perd ses 64px de retrait haut : c'est l'espace qu'une page
   prend sous la barre de navigation, or il n'y a plus de barre dans un cadre. */
body.embed .wrap-narrow{ padding-left:0; padding-right:0; max-width:none; }
body.embed .page-head{ padding-top:0; padding-bottom:24px; }
/* ⚠️ ET SON TITRE REDESCEND À LA TAILLE D'UN TITRE DE PANNEAU (2026-08-30,
   demande Julie : « réduis le texte de cet onglet et enlève le gras du
   titre »). En page, `clamp(30px, 4vw, 44px)` en graisse 600 est une
   ACCROCHE ; dans un panneau de réglages, il arrive sous un titre de page
   qui fait 30px et le double. On reprend donc exactement `.ap-modal-title`
   — Lora, 23px, graisse NORMALE — la convention des écrans de compte
   (« c'est un en-tête d'écran de réglages, pas une accroche »).
   ⚠️ Portée `body.embed` seulement : la page servie EN DIRECT garde son
   grand titre. /supprimer-mon-compte est publique et indexable, c'est
   l'URL déclarée à Google Play — elle ne doit pas rétrécir avec le
   panneau qui l'embarque. */
body.embed .page-head h1{ font-size:23px; font-weight:400; letter-spacing:-0.4px;
  line-height:1.25; margin-bottom:8px; }
body.embed .page-head p{ font-size:13.5px; line-height:1.6; }

/* ═══ LA BARRE D'APPLICATION DU SITTER (2026-08-27, maquette Julie)
   Marque NEKO · pastilles des deux espaces · identité du compte à droite.
   Portée : /espace-sitter et /moyens-de-paiement, les deux écrans du sitter.
   ⚠️ ELLE VIT ICI, DANS SHARED.CSS, ET PAS DANS LE <style> DES DEUX PAGES :
   elles doivent être indiscernables, et les feuilles de page sont inlinées —
   elles ne se voient pas l'une l'autre. Deux copies divergeraient, ce qui est
   exactement ce qui est arrivé à la carte d'onglets du 26/08, portée en double
   et reprise deux fois. Ce que chaque page garde chez elle, c'est son
   ASSEMBLAGE (où commencent ses colonnes, sa carte du milieu), pas la barre.
   ⚠️ La barre vit HORS du wrap de sa page : la marque doit toucher le bord
   gauche de l'écran, or le wrap est décalé de la largeur du menu vertical.
   ⚠️ Son padding intérieur RECOPIE celui du wrap (28px, 16px sous 560px), pour
   que la marque s'aligne sur le contenu là où il n'y a pas de menu.
   ⚠️ `display:block` EST INDISPENSABLE et n'est pas une valeur par défaut
   écrite pour rien : `.sdash-head` a porté un `display:flex` dans le <style> de
   /espace-sitter jusqu'au 2026-08-27 (l'ancien en-tête). Une feuille de page
   étant déclarée APRÈS le <link> de celle-ci, elle l'emportait — la barre
   n'étirait pas son unique enfant et tout se tassait à gauche. La règle a été
   retirée là-bas ; ne pas en réintroduire d'équivalent sans repasser ici. */
.sdash-head{ display:block; background:var(--white); border-bottom:1px solid var(--line); }
.sdash-head-in{ display:flex; align-items:center; gap:18px; min-height:56px; padding:0 28px; }
.sdash-brand{
  font-family:var(--mono); font-size:12px; font-weight:700; letter-spacing:.26em;
  text-transform:uppercase; color:var(--ink); text-decoration:none; flex:0 0 auto;
}
/* ⚠️ LES DEUX ESPACES EN PASTILLES, et non en gros CTA corail (2026-08-27). Le
   dessin est celui d'un onglet de barre d'application : filet discret, angles à
   9px, la page courante en aplat crème. Ne PAS y remettre `.neko-side-cta`
   (corail, liseré encre remonté, tranche 3D) — ce dessin-là est fait pour une
   colonne de menu posée sur du crème, pas pour une barre blanche. Il a été
   essayé le même jour, en tête de carte puis sur le panneau du haut : deux
   boutons de 60px y pesaient plus lourd que la page qu'ils surmontaient.
   ⚠️ `is-here` marque la page COURANTE et reste cliquable : il ramène en haut
   de sa propre page, là où le désactiver retirerait un repère sans rien offrir.
   ⚠️ Les destinations RECOPIENT le menu (`NEKO_SITTER_TABS`, shared.js) :
   `/espace-sitter` et `/moyens-de-paiement#profil`. Si l'une bouge là-bas, elle
   bouge dans les deux barres — sinon la barre et le menu mènent ailleurs. */
.sdash-navpills{ display:flex; align-items:center; gap:8px; flex:0 0 auto; min-width:0; }
.sdash-navpill{
  display:inline-flex; align-items:center; height:32px; padding:0 14px;
  border:1px solid var(--line); border-radius:9px;
  background:var(--white); color:var(--ink-soft);
  font-family:"Lora", Georgia, serif; font-size:13.5px; font-weight:500;
  text-decoration:none; white-space:nowrap;
  transition:background .16s, color .16s, border-color .16s;
}
.sdash-navpill:hover{ color:var(--ink); border-color:var(--ink-soft); }
.sdash-navpill.is-here{ background:var(--cream-2); color:var(--ink); }
/* PAS de fond sous l'identité : elle est déjà sur le blanc de la barre. Le
   dessin vient de `.neko-side-*` et n'est pas recopié. `min-height:0` neutralise
   les 52px de `.neko-side-userline`, calés sur la hauteur d'une entrée de
   menu : hors du menu, ils ne réservent la place de rien. */
.sdash-head .sdash-me{ min-height:0; gap:16px; }
@media (max-width:560px){ .sdash-head-in{ padding:0 16px; gap:12px; } }
@media (max-width:420px){ .sdash-navpill{ padding:0 10px; font-size:12.5px; } }
/* ⚠️ La marque part sous 1025px : le bouton hamburger est fixé au coin
   haut-gauche et passerait dessus. Le format appli n'a jamais porté de marque en
   tête, il porte le dock en pied. */
/* ⚠️ LA BARRE N'EXISTE PAS EN FORMAT APPLI (2026-08-27, demande Julie :
   « je n'ai jamais dit de toucher au format tablette — enlève Mes gardes, Mon
   activité et la barre blanche du haut »). Elle n'était masquée qu'à moitié en
   dessous de 1025px (seule la marque partait), et ce qui restait était un pur
   DOUBLON : le dock du bas (`NEKO_SITTER_DOCK`, shared.js) porte déjà les
   quatre mêmes portes — Mes gardes, Messages, Mon activité, Mon compte — et le
   compte s'ouvre par la dernière. Rien ne devient inatteignable en la retirant.
   ⚠️ Le format appli n'a jamais porté de barre en tête : il porte le dock en
   pied. Ne pas la réafficher « pour rappeler la page courante », c'est le rôle
   de l'onglet allumé du dock.
   ⚠️ `display:none` et non un retrait du markup : la barre est montée par
   shared.js pour les deux écrans du sitter, et elle reste entière ≥ 1025px. Les
   deux pages mesurent sa hauteur (`--sdash-rails-top` / `--pm-rails-top`) pour
   caler leurs colonnes ; masquée elle mesure 0, et ces variables ne servent
   qu'au-dessus de 1025px. */
@media (max-width:1024px){
  .sdash-head{ display:none; }
  .sdash-head-in{ gap:12px 16px; flex-wrap:wrap; }
  .sdash-brand{ display:none; }
}
@media (min-width:1025px){
  /* Fixée : elle ne défile pas, et elle ne réserve donc aucune place — c'est
     chaque page qui mesure sa hauteur et décale son contenu d'autant. */
  .sdash-head{ position:fixed; top:0; left:0; right:0; z-index:70; }
  .sdash-head-in{ min-height:60px; }
  /* Le compte est poussé à droite : le cluster `#dash-actions`, entre les deux,
     est vidé en desktop donc masqué par `.dash-nav-actions:empty`. */
  .sdash-head .sdash-card-me{ margin-left:auto; flex:0 0 auto; }
}
/* ⚠️ L'override `.neko-side-shortcuts + .neko-tab-list{border-top:0}` a été
   RETIRÉ le 2026-07-31 : il existait quand le filet était porté par les
   raccourcis eux-mêmes (il aurait fait une double ligne). Le filet ayant changé
   de camp, la liste garde le sien dans tous les cas. */
/* --- EN-TÊTE = PORTE-TITRE DE PAGE, MARQUE EN PIED (2026-08-24, demande
   Julie ; renverse le choix du 2026-07-31 qui mettait la marque en tête) -----
   La première ligne du menu est `#neko-side-title`, remplie par la page via
   nekoSetSideTitle (« Le carnet de <chat> » sur /mon-espace) ; vide, elle est
   `hidden` et le menu commence aux raccourcis. Même dessin que le h1 qu'elle
   remplace (Lora, <em> corail pour le prénom du chat), taillé pour la colonne :
   le titre PEUT passer sur deux lignes ici, la colonne n'a pas la contrainte
   « ne pas pousser la page » qui impose l'ellipse au h1. */
.neko-side-title{
  font-family: var(--serif); font-weight: 600; font-size: 22px;
  letter-spacing: -.02em; line-height: 1.15; color: var(--ink);
  padding: 2px 2px 6px;
}
/* ⚠️ Nécessaire, pas décoratif : `.neko-side-only{display:block}` est une règle
   AUTEUR, elle écrase le `[hidden]{display:none}` de la feuille UA quelle que
   soit la spécificité — sans cette ligne, le slot vide resterait un bloc de
   22px de padding en tête de menu sur toute page qui ne pose pas de titre. */
.neko-side-title[hidden]{ display: none; }
/* Le RÔLE (« Propriétaire » / « Cat-sitter » / « Home sitter ») sous le titre
   (2026-08-24, demande Julie) : il vivait dans le bloc de marque
   (.neko-side-brand-tag, supprimée), il est descendu au pied avec elle, et la
   demande le ramène en tête, sous « Le carnet de <chat> ». Toujours visible :
   sur une page sans titre il redevient la première ligne du menu. Même dessin
   de micro-libellé que les intertitres de groupe (.neko-side-group). */
.neko-side-role{
  margin: 0; padding: 0 2px 18px;
  font-family: var(--sans); font-size: 9.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--muted); white-space: nowrap; line-height: 1;
}

/* --- PANNEAU CONTEXTUEL DU MENU (2026-08-24, maquette dashboard) ------------
   Posé par la page via nekoSetSideContext (shared.js). Quand il est là, le menu
   range sa navigation : raccourcis et liste d'entrées masqués. Dessin repris de la maquette : une carte récap SOMBRE (la seule
   surface encre du site, elle porte l'avancement), une liste de sections
   dessous, une note discrète en pied. */
.neko-side-ctx[hidden]{ display: none; }
.neko-tabbar.has-side-ctx .neko-side-shortcuts,
.neko-tabbar.has-side-ctx .neko-tab-list{ display: none; }
/* ⚠️ `.neko-side-foot` n'est plus dans cette liste : le pied « communauté » est
   supprimé (2026-08-27). Et il n'y a plus de bascule de `margin-top:auto` à
   faire ici — il est porté en toutes circonstances par le bloc d'alertes, ou
   par la marque à défaut (cf. le § du bas de colonne, plus haut). */

/* ══ NOTIFICATIONS DU CAT-SITTER EN PIED DE MENU (2026-08-27, demande Julie) ══
   Les deux interrupteurs de `nekoSitterAlertsMarkup()` (shared.js), posés dans
   la sidebar par neckoBottomNav. Le dessin des lignes et de la bascule vient du
   bloc `.neko-alert` / `.neko-switch` plus haut : ON NE LE REDÉFINIT PAS, on le
   RESSERRE — la colonne n'a que ~256px utiles contre la pleine largeur d'un
   panneau, et le sous-titre y passait sur trois lignes.
   ⚠️ Préfixe `.neko-tabbar` OBLIGATOIRE : sans lui ces règles (0,1,0) perdraient
   contre `.neko-side-only{display:block}` déclarée plus haut, et le bloc
   cesserait d'être une colonne flex. C'est aussi ce qui garantit qu'elles ne
   touchent PAS le porteur mobile (`#pm-alerts` de /moyens-de-paiement), qui
   garde le dessin large. */
.neko-tabbar .neko-side-alerts{ display: flex; flex-direction: column; padding-top: 14px; }
.neko-tabbar .neko-side-alerts .neko-side-group{ padding-top: 0; }
.neko-tabbar .neko-alerts{ gap: 8px; }
.neko-tabbar .neko-alert{ padding: 9px 11px; border-radius: 13px; gap: 9px; }
.neko-tabbar .neko-alert-txt b{ font-size: 12.5px; }
.neko-tabbar .neko-alert-txt small{ font-size: 10.5px; line-height: 1.3; }
/* Bascule au 3/4 : 40×24 à côté de deux lignes de 10.5px déséquilibrait la ligne. */
.neko-tabbar .neko-switch{ width: 34px; height: 20px; }
.neko-tabbar .neko-switch i::after{ top: 3px; left: 3px; width: 14px; height: 14px; }
.neko-tabbar .neko-switch input:checked + i::after{ transform: translateX(14px); }
/* Cran 2 : on resserre encore, comme le reste du menu. */
.neko-tabbar.is-tight .neko-side-alerts{ padding-top: 10px; }
.neko-tabbar.is-tight .neko-alert{ padding: 7px 10px; }
.neko-tabbar.is-tight .neko-alert-txt small{ display: none; }
/* Carte récap sombre : nom du chat, pastilles des autres chats, avancement,
   ligne de garde. Le corail de la jauge est le seul accent sur l'encre. */
.sctx-card{
  background: var(--ink); color: #fff; border-radius: 18px;
  padding: 14px 16px 15px; margin-bottom: 14px;
  box-shadow: 0 16px 40px -22px rgba(31, 26, 26, .45);
}
/* La LISTE a sa propre bulle blanche (2026-08-24, demande Julie — la carte
   sombre et elle sont deux surfaces détachées, le menu derrière est
   transparent en mode contextuel, cf. la règle `.neko-tabbar.has-side-ctx`
   dans le bloc ≥1025px). Posée par les builders de /mon-espace autour du
   groupe + liste + note. */
.sctx-panel{
  background: var(--white); border: 1px solid var(--line); border-radius: 18px;
  padding: 16px 12px 14px;
  box-shadow: 0 12px 32px -20px rgba(31, 26, 26, .3);
}
.sctx-panel + .sctx-panel{ margin-top: 12px; }
/* Mini-bulle d'avancement (maquette 5a) : « Consignes prêtes 5/6 » + jauge sur
   FOND BLANC — les couleurs de `.sctx-prog-l` et `.sctx-track` ci-dessus sont
   celles de la carte sombre, on les rebascule ici. */
.sctx-mini{ padding: 13px 14px 14px; margin-bottom: 12px; }
.sctx-mini .sctx-prog-l{ color: var(--muted); font-size: 11px; }
.sctx-mini .sctx-prog-l b{ color: var(--ink); }
.sctx-track.is-light{ background: rgba(31, 26, 26, .12); height: 5px; margin-top: 7px; }
/* Libellé tronqué + résumé à droite d'une ligne de section (« 2 repas »,
   « 1×/jour »…), le résumé n'apparaissant que sur une consigne renseignée. */
.sctx-lbl{ min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sctx-sum{
  margin-left: auto; flex: 0 0 auto; max-width: 84px;
  font-size: 10px; font-weight: 600; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sctx-head{ display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.sctx-name{
  font-family: var(--serif); font-size: 17px; font-weight: 600; letter-spacing: -.01em;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sctx-pill{
  -webkit-appearance: none; appearance: none; cursor: pointer;
  flex: 0 0 auto; padding: 5px 10px; border-radius: 999px;
  border: 1px solid rgba(255,255,255,.35); background: transparent;
  color: #fff; font-family: var(--sans); font-size: 10.5px; font-weight: 600; line-height: 1;
  transition: border-color .15s ease;
}
.sctx-pill:hover{ border-color: #fff; }
.sctx-prog{ margin-top: 12px; }
.sctx-prog-l{
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  font-size: 10.5px; color: rgba(255,255,255,.75);
}
.sctx-prog-l b{ font-family: var(--mono); font-size: 10.5px; font-weight: 700; color: #fff; }
.sctx-track{ margin-top: 6px; height: 4px; border-radius: 2px; background: rgba(255,255,255,.18); overflow: hidden; }
.sctx-fill{ display: block; height: 100%; border-radius: 2px; background: var(--terracotta); transition: width .3s ease; }
.sctx-sub{
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,.14);
  font-size: 11px; color: rgba(255,255,255,.75);
}
.sctx-sub b{ color: #fff; font-weight: 600; }
/* Liste de sections : mêmes proportions que les entrées .neko-tab, en plus
   compact — pastille-point à gauche (corail quand la section est renseignée),
   ligne active sur fond rosé. ⚠️ Texte corail = var(--terracotta), jamais
   var(--terracotta-deep) (règle transversale du repo). */
.sctx-group{
  margin: 0; padding: 0 4px 8px;
  font-family: var(--sans); font-size: 9.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--muted); line-height: 1;
}
.sctx-list{ display: flex; flex-direction: column; gap: 2px; }
.sctx-item{
  -webkit-appearance: none; appearance: none; border: 0; background: none; cursor: pointer;
  display: flex; align-items: center; gap: 10px; text-align: left; width: 100%;
  padding: 0 12px; min-height: 40px; border-radius: 10px;
  font-family: var(--sans); font-size: 13px; font-weight: 600; letter-spacing: -.1px;
  color: var(--ink-soft);
  transition: background .15s ease, color .15s ease;
}
.sctx-item:hover{ background: var(--cream-2); color: var(--ink); }
.sctx-item.on{ background: var(--card-rose); color: var(--terracotta); }
.sctx-dot{ flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; background: rgba(31,26,26,.25); transition: background .15s ease; }
.sctx-item.done .sctx-dot,
.sctx-item.on .sctx-dot{ background: var(--terracotta); }
.sctx-note{ margin: 14px 4px 0; font-size: 10.5px; line-height: 1.5; color: var(--muted); }
.neko-side-title em{ color: var(--terracotta); font-weight: 500; }
/* La MARQUE, elle, est descendue TOUT EN BAS du menu, sous « Déconnexion »
   (dernier enfant du <nav>). Le motif de 2026-07-31 tient à moitié : elle doit
   exister quelque part (les pages connectées n'affichent plus la barre du haut,
   donc sans elle plus aucun logo ni retour à l'accueil) — ce quelque part est
   désormais le pied.
   ⚠️ C'est un lien vers `/` : ne pas le repointer vers le profil, c'est le seul
   chemin de retour au site vitrine depuis un espace connecté. */
/* EN COLONNE ET CENTRÉE (2026-08-24, demande Julie) : le logo au-dessus, NEKO
   en dessous, les deux centrés dans la colonne — et le mot quitte la Lora pour
   la SANS en petites capitales espacées (« typo plus moderne »). Le
   `margin-right` négatif compense l'espace que letter-spacing ajoute après la
   dernière lettre, sans lui le mot paraît décalé à gauche du logo. */
.neko-side-brand{
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-decoration: none; padding: 14px 2px 2px;
}
.neko-side-brand-mark{
  width: 52px; height: 52px; flex: 0 0 auto; display: block;
}
.neko-side-brand-mark img{
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.neko-side-brand-name{
  font-family: var(--sans); font-size: 11.5px; font-weight: 700;
  color: var(--ink); letter-spacing: .32em; margin-right: -.32em;
  text-transform: uppercase; line-height: 1;
}
/* ⚠️ `.neko-side-brand-tag` A ÉTÉ SUPPRIMÉE (2026-08-24) : le rôle est remonté
   sous le titre de page (.neko-side-role, plus haut). Ne pas remettre de
   sous-titre à la marque — elle n'est plus qu'une signature de pied. */

/* --- Intertitres de groupe (« Ma garde », « Compte ») -----------------------
   ⚠️ Ce sont eux qui remplacent les icônes retirées : sans icône ET sans
   groupe, la liste redevient un bloc de cinq liens indistincts. Émis par
   neckoBottomNav au changement de `group` — le texte vient de NEKO_*_TABS, pas
   du CSS. */
.neko-side-group{
  margin: 0; padding: 0 4px 6px;
  font-family: var(--sans); font-size: 9.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .14em;
  color: var(--muted); line-height: 1;
}
/* Respiration AVANT chaque groupe sauf le premier (qui suit déjà le filet du
   bloc de raccourcis). */
.neko-tab + .neko-side-group{ padding-top: 20px; }

/* --- Liste des onglets : du texte, très espacé --- */
.neko-tab-list{
  display: flex; flex-direction: column; align-items: stretch;
  gap: 2px; width: 100%;
  border-top: 1px solid var(--line);
  padding-top: 14px;
}
/* ⚠️ « Messages » — HISTOIRE À QUATRE TEMPS, et l'entrée N'EXISTE PLUS DANS
   AUCUN DES DEUX MENUS. Retirée une première fois (doublon de la capsule
   nekoMountFabDock), REVENUE le 2026-08-02 (« la capsule n'est qu'une icône
   détachée du reste, et le menu est l'endroit où l'on cherche où aller »),
   RETIRÉE CÔTÉ PROPRIÉTAIRE le 2026-08-22 (demande Julie) au profit de
   Parrainage / Actualités / Avis Google, puis CÔTÉ SITTER le 2026-08-26 (même
   demande, mêmes mots : « supprime, ça fait doublon »).
   ⚠️ Ce que le retrait a coûté côté propriétaire et comment c'était payé : cette
   entrée était la seule porte vers la messagerie entre 768 et 1024px sur les
   pages à rail, d'où le rallumage de la capsule flottante le même jour.
   ⚠️ CE RATTRAPAGE EST SANS OBJET DEPUIS LE 2026-08-26 : les deux rôles ont le
   dock du bas jusqu'à 1024px, la bande sans porte n'existe plus (cf.
   `body.has-owner-rail .neko-fab-dock`, plus haut dans ce fichier, et le pavé de
   NEKO_SITTER_TABS pour les trois portes qui restent).
   ⚠️ Le compteur de non-lus reste câblé sur `.neko-tabbar
   .neko-tab[data-id="messages"]` (shared.js) : il ne trouve simplement plus
   d'hôte, dans aucun des deux rôles, et `setBadge` sort sans rien faire. Celui
   du DOCK est un autre sélecteur, et lui reste servi. */
.neko-tab{
  position: relative;
  -webkit-appearance: none; appearance: none;
  background: none; border: 0; cursor: pointer;
  text-decoration: none;
  flex: 0 0 auto;
  display: flex; flex-direction: row; justify-content: flex-start; align-items: center;
  gap: 12px;
  /* ⚠️ 52px de haut et un retrait gauche de 18px qui laisse la place à la
     pastille de l'entrée active (2026-07-30). C'est ici que passe l'espace
     libéré par l'en-tête : cinq entrées à 46px collées les unes aux autres se
     lisaient comme un bloc, pas comme une liste. */
  padding: 0 14px 0 18px; min-height: 52px;
  border-radius: 12px;
  /* ⚠️ SANS-SERIF (2026-07-31). Le menu a porté Lora du 2026-07-29 au
     2026-07-31, au motif qu'une liste de destinations s'accorde aux titres de
     page qu'elle annonce. Retiré sur demande explicite : à 14,5px et en gras,
     la Lora s'empâte, et une colonne de cinq lignes serif se lit comme un
     sommaire de document, pas comme une navigation. La serif reste aux titres.
     Ne pas la remettre ici. */
  font-family: var(--sans);
  font-size: 13.5px; font-weight: 600;
  letter-spacing: -.15px;
  color: var(--ink-soft);
  min-width: 0; text-align: left;
  transition: background .15s ease, color .15s ease;
}
/* ⚠️ Menu SANS icônes (2026-07-29, demande explicite) : la liste se lit en
   texte seul, l'entrée courante étant portée par une pilule encre pleine. Les
   icônes restent dans le DOM (`NEKO_TAB_ICONS` sert aussi au dock du bas, où
   elles sont indispensables) — c'est ici, et ici seulement, qu'on les masque. */
.neko-tab .tab-ico{ display: none; }
.neko-tab span{ line-height: 1; }
/* ⚠️ Survol en encre transparente, plus var(--cream-2) (2026-07-30) : c'est
   exactement la couleur du menu depuis qu'il est beige, le survol avait donc
   cessé d'exister. Une alpha d'encre fonctionne sur n'importe quel fond. */
.neko-tab:hover{ background: rgba(31,26,23,0.055); color: var(--ink); }
/* ⚠️ ENTRÉE ACTIVE = FILET CORAIL à gauche + fond très pâle + texte encre gras
   (2026-07-31, mise en page « éditoriale »). Elle a d'abord été une pilule encre
   pleine (un bloc noir de 200px au milieu d'une colonne claire, qui attirait
   l'œil plus que la page qu'il désigne), puis une pastille ronde de 6px
   (2026-07-30), trop discrète une fois les icônes retirées et les entrées
   groupées : à cinq entrées de même graisse, le point se cherchait.
   ⚠️ Ne pas revenir au fond corail pâle des origines : il se confondait avec le
   survol, et deux entrées paraissaient actives en même temps. Le fond est ici
   une alpha d'ENCRE, plus claire que le survol, et c'est le filet qui porte la
   couleur. */
.neko-tab.active{ background: rgba(31,26,23,0.035); color: var(--ink); font-weight: 700; }
.neko-tab.active:hover{ background: rgba(31,26,23,0.055); }
.neko-tab.active::before{
  content: ""; position: absolute; left: 0; top: 50%;
  width: 3px; height: 22px; margin-top: -11px; border-radius: 0 2px 2px 0;
  background: var(--terracotta);
}
/* Urgence : la seule entrée du menu qui se démarque au repos. Un filet corail à
   gauche — assez pour l'attraper d'un coup d'œil en panique, sans remplir le
   menu d'aplats.
   ⚠️ En `box-shadow` et non en ::before : ce pseudo-élément porte désormais la
   pastille de l'entrée active, et l'urgence peut être active. */
.neko-tab[data-id="urgence"]{ box-shadow: inset 3px 0 0 0 var(--terracotta); }
.neko-tab-badge{ margin-left: auto; min-width: 18px; height: 18px; border-radius: 9px; background: var(--terracotta); color: #fff; font-size: 10px; font-weight: 700; display: grid; place-items: center; padding: 0 5px; font-family: var(--sans); line-height: 1; }

/* ⚠️ LE PIED « COMMUNAUTÉ » A ÉTÉ SUPPRIMÉ (2026-08-27, demande Julie :
   « supprime »). C'étaient les trois vignettes de chats, l'accroche « Chinez,
   donnez, entre amoureux des chats » et le lien « Découvrir ↗ » vers
   /petite-annonces, épinglés en bas de la colonne.
   Sont parties avec lui les classes `.neko-side-foot`, `.neko-side-avatars`,
   `.neko-side-community`, `.neko-side-visit`, et le markup de `neckoBottomNav`.
   ⚠️ IL PORTAIT LE `margin-top:auto` DE LA COLONNE : c'est lui qui poussait tout
   le pied du menu vers le bas. Le rôle est REPRIS par le bloc « Notifications »
   du sitter et, à défaut, par la marque (cf. le § qui suit `.neko-tab-badge`).
   Retirer ce report ferait remonter le logo se coller sous la dernière entrée.
   ⚠️ IL ÉTAIT AUSSI TOUT LE CRAN 1 de `nekoFitSideMenu` (`is-short`), qui n'avait
   que lui à sacrifier sur un écran court. Le cran est supprimé de l'échelle
   (shared.js) : elle n'en compte plus que deux, `is-tight` puis `is-scroll`.
   ⚠️ Ce bloc a DÉJÀ été supprimé une fois, le 2026-07-30, puis rétabli le
   2026-07-31 sur demande explicite. La demande d'aujourd'hui est la plus
   récente : ne pas le rétablir en relisant le commentaire de juillet. */

/* Le bas de la colonne, maintenant que le pied « communauté » n'y est plus.
   ⚠️ UN SEUL `margin-top:auto` à la fois : deux blocs en `auto` dans une colonne
   flex se PARTAGENT l'espace libre, et les interrupteurs finiraient au milieu.
   D'où l'adjacence — dans le markup (neckoBottomNav) la marque suit
   IMMÉDIATEMENT le bloc d'alertes, quand il existe. */
.neko-side-alerts{ margin-top: auto; }
.neko-side-alerts + .neko-side-brand{ margin-top: 0; }
.neko-side-brand{ margin-top: auto; }

/* --- LIGNE D'IDENTITÉ DANS LA LISTE (2026-08-24, demande Julie) -------------
   ⚠️ `.neko-side-user` (le pied d'identité, né le 2026-07-31) est SUPPRIMÉE :
   l'identité partage désormais la ligne « Déconnexion » de la liste, rendue par
   la branche 'logout' de neckoBottomNav — avatar + prénom à gauche, la sortie
   à droite. Les classes internes (.neko-side-me, .neko-side-uavatar,
   .neko-side-uname, .neko-side-logout) SURVIVENT : c'est la ligne qui change,
   pas ses morceaux. Padding gauche 18px = celui des `.neko-tab`, pour que
   l'avatar s'aligne sur les libellés d'entrées. */
.neko-side-userline{
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 4px 14px 4px 18px; min-height: 52px;
}
.neko-side-me{
  display: flex; align-items: center; gap: 9px;
  text-decoration: none; min-width: 0; flex: 1 1 auto;
}
.neko-side-uavatar{
  width: 28px; height: 28px; border-radius: 50%; flex: 0 0 auto;
  padding: 1.5px; background: var(--cream);
  box-shadow: 0 0 0 1.5px var(--terracotta);
  display: block;
}
.neko-side-uavatar img{
  width: 100%; height: 100%; border-radius: 50%;
  object-fit: cover; display: block;
}
/* Rond aux initiales quand le compte n'a pas de photo (cf. nekoSideAvatarInner). */
.neko-side-initial{
  width: 100%; height: 100%; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--terracotta), var(--terracotta-deep));
  color: #fff; font-family: var(--serif); font-weight: 700;
  font-size: 13px; line-height: 1;
}
.neko-side-uname{
  font-family: var(--serif); font-size: 13.5px; font-weight: 600;
  color: var(--ink); line-height: 1.2; letter-spacing: -.1px;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.neko-side-me:hover .neko-side-uname{ color: var(--terracotta); }
/* Sortie discrète : du texte, jamais un bouton plein — elle ne doit pas
   concurrencer les CTA du haut. */
.neko-side-logout{
  -webkit-appearance: none; appearance: none;
  background: none; border: 0; padding: 0; cursor: pointer; flex: 0 0 auto;
  font-family: var(--sans); font-size: 11.5px; font-weight: 500;
  color: var(--muted); line-height: 1;
}
.neko-side-logout:hover{ color: var(--terracotta); }

/* ═══ SHELL MOBILE — keyé sur `body.neko-mobile-shell`, PLUS sur la seule
   media query (2026-08-24, demande Julie : « en format tablette le dashboard
   owner doit être comme le format mobile, avec le menu en bas » ; ÉTENDU AU
   SITTER le 2026-08-26, même demande, même mot pour mot). La classe est posée
   par nekoShellModeSync (shared.js) : LES DEUX RÔLES jusqu'à 1024px inclus, la
   tablette est un grand téléphone. La media (max-width:1024px) qui l'enveloppe
   n'est qu'une ceinture, la classe ne pouvant jamais être vraie au-dessus.
   ⚠️ « SITTER jusqu'à 767px (inchangé) » est ABROGÉ (il a valu du 24 au 26
   août) : ne pas le restaurer en relisant un texte daté. La classe reste
   pourtant le point de keyage bien que les deux rôles partagent désormais le
   même nombre — c'est elle qui garde le seuil déplaçable en un seul endroit.
   Contenu du mode : le tiroir couvre l'écran ; pas de padding bas (plus de
   pilule). Le hamburger flotte en haut à gauche : on ne réserve PAS de bande en
   haut (les titres restent tout en haut) — à la place, chaque en-tête de page
   se décale à droite via .neko-menu-clear pour ne pas passer sous le bouton. */
@media (max-width: 1024px){
  /* ⚠️ `padding-left: 0` neutralise les 296px de la sidebar (bloc ≥768px plus
     bas) quand un PROPRIÉTAIRE est en tablette : les deux règles ciblent le
     même body, celle-ci gagne à la spécificité. Sous 768px il ne coûte rien. */
  body.neko-mobile-shell.has-tabbar{ padding-top: 8px; padding-bottom: 0; padding-left: 0; }
  /* Décale les blocs de titre à droite du hamburger (largeur bouton ≈ 58px). */
  body.neko-mobile-shell.has-tabbar .neko-menu-clear{ padding-left: 58px; }

  /* ⚠️ LES DEUX RÔLES (owner 2026-07-26, sitter 2026-07-30) : plus de hamburger
     en mobile, le dashboard est un miroir de l'appli — la barre du bas
     (.neko-dock) est TOUTE la navigation. `body.no-burger` est posée par
     nekoMountBottomNav() ; le bouton et son voile ne sont même pas montés, seul
     le menu vertical l'est encore parce qu'il sert de sidebar DESKTOP. D'où ces
     règles enfermées dans le breakpoint mobile : au-delà de 768px la sidebar
     doit réapparaître intacte, dans les deux rôles.
     ⚠️ La classe est conservée (plutôt que d'appliquer ces règles à tout le
     monde) : c'est elle qui documente le choix, et une page qui voudrait le
     tiroir n'aurait qu'à ne pas la poser. */
  body.neko-mobile-shell.no-burger .neko-tabbar,
  body.neko-mobile-shell.no-burger .neko-tabbar-backdrop{ display: none; }
  /* Plus de bouton en haut à gauche → plus de bande à réserver : sans ça les
     titres des pages partagées (/bons-plans, /profil-proprietaire, /urgence…)
     resteraient décalés de 58px pour contourner un bouton absent. */
  body.neko-mobile-shell.no-burger.has-tabbar .neko-menu-clear{ padding-left: 0; }
  /* ⚠️ SAUF quand la classe est posée SUR le `.wrap` lui-même (2026-08-02).
     `.neko-menu-clear` sert deux familles d'éléments :
       — un bloc de titre DÉJÀ dans un `.wrap` (/bons-plans, /profil-sitter,
         /urgence…), qui n'a pas de retrait propre : le remettre à 0 est juste ;
       — le `.wrap` LUI-MÊME (/profil-public-sitter, /profil-public-proprietaire),
         qui porte `padding: 0 28px`. Là, `padding-left: 0` n'annulait pas les
         58px du hamburger, il effaçait AUSSI le retrait de la page : le fil
         d'Ariane venait se coller au bord de l'écran pendant que les cartes
         restaient en retrait, et se lisait comme sorti du cadre. Il n'était pas
         mal aligné avec le reste — il était sorti du gabarit.
     ⚠️ La valeur doit rester celle de `.wrap` ci-dessus : les deux se lisent
     ensemble, et 28px ici sert précisément à retomber dessus. */
  body.neko-mobile-shell.no-burger.has-tabbar .wrap.neko-menu-clear{ padding-left: 28px; }
  /* Mobile : Notifications revient dans le header (à côté de « Bonjour marc »)
     → retirée du menu pour éviter le doublon. (Messages est déjà masqué partout.) */
  body.neko-mobile-shell .neko-tab[data-id="notifs"]{ display: none; }
  /* ⚠️ MESSAGERIE = un ÉCRAN, pas un pop-up (2026-07-27, MOBILE UNIQUEMENT).
     Miroir de l'appli : « Messages » est un onglet de la barre du bas qui ouvre
     une page pleine (mobile/app/messages.tsx), pas un panneau posé par-dessus la
     page courante. Le DOM ne change pas : le tiroir reste la SEULE messagerie du
     site depuis la suppression de /messages et /conversation, et il garde son
     adresse `?messagerie=1`, et c'est le CSS qui le transforme : pleine largeur,
     pas de voile, pas d'ombre latérale, pas de bord arrondi.
     ⚠️ HORS du mode `neko-mobile-shell`, le panneau coulissant reste INCHANGÉ
     (les deux rôles au-dessus de 1024px) : la demande porte sur le format
     appli, et en desktop la messagerie cohabite avec le contenu (elle est même
     ouverte en permanence dans la colonne de droite des deux dashboards, cf.
     `body.neko-msg-pinned` — le dock propre à /espace-sitter, `body.msg-docked`,
     a été supprimé le 2026-08-23).
     Le dock reste visible SOUS la liste : sur l'appli la barre du bas ne
     disparaît pas quand on est sur l'onglet Messages. */
  body.neko-mobile-shell #neko-msg-backdrop{ display: none; }
  body.neko-mobile-shell .neko-msg-drawer{
    width: 100%; left: 0; right: 0;
    top: 0; bottom: 0; height: auto;
    border-left: 0; box-shadow: none;
  }
  body.neko-mobile-shell.has-dock .neko-msg-drawer{
    bottom: calc(var(--neko-dock-h) + env(safe-area-inset-bottom, 0px));
  }
  /* ⚠️ Un FIL ouvert garde le dock, lui aussi (2026-07-29). Le fil l'a masqué du
     2026-07-27 au 2026-07-29, en miroir de conversation.tsx qui pousse un écran
     plein : combiné à la croix retirée ci-dessous, l'en-tête du fil perdait AUSSI
     sa flèche « Retour » (les deux portent la classe .neko-drawer-close), et
     l'écran n'avait plus aucune sortie. Le dock est la navigation du site en
     mobile : il reste visible, et la barre de saisie se pose AU-DESSUS.
     Rien à écrire ici : la règle `body.has-dock .neko-msg-drawer` ci-dessus
     réserve déjà la bande du dock, en liste comme en fil.
     ⚠️ Clavier ouvert : sur Chrome Android le viewport se réduit, dock et saisie
     remontent ensemble et « Envoyer » reste au-dessus des onglets ; sur iOS
     Safari les deux passent derrière le clavier (le navigateur y ramène le champ
     focalisé), ce qui vaut pour n'importe quel élément `fixed` de la page. */
  body.neko-mobile-shell.has-dock .neko-msg-compose{
    /* Le dock couvre déjà la zone sûre : sans ce retrait la marge basse compte
       double. ⚠️ Conditionné à `has-dock` : sans session le tiroir descend à
       `bottom: 0` et cette marge redevient la seule protection contre le home
       indicator iOS. */
    padding-bottom: 14px;
  }

  /* --- En-tête de la LISTE, copie de mobile/app/messages.tsx (2026-07-27) ---
     Titre « Messages » à gauche, pillule corail « Berlioz · IA » à droite. La
     carte chatbot du pied appartient au panneau desktop : ici elle ferait
     doublon avec la pillule.
     ⚠️ Il ne reste ici que les `display` : le DESSIN du titre et de la pillule a
     été remonté hors media query le 2026-08-15, la colonne épinglée le servant
     lui aussi. Ne pas le redescendre ici, ça le remettrait en double.
     ⚠️ La règle qui masquait le sur-titre « Messagerie » est partie le
     2026-08-15 : il a été supprimé du DOM (cf. `nekoRenderMsgList`), il n'y a
     plus rien à masquer. */
  body.neko-mobile-shell .neko-msg-drawer .neko-drawer-head{ padding: 18px 22px 14px; border-bottom: 0; }
  /* ⚠️ Le titre n'a plus de règle ici : il est visible par défaut depuis le
     2026-08-15, et il porte déjà les 26px du mobile hors media query. */
  body.neko-mobile-shell .neko-msg-botpill{ display: inline-flex; }
  /* La croix ne referme qu'un panneau posé sur la page. En mobile c'est un écran
     plein, et le dock du bas en est la sortie (comme l'appli, où Messages est un
     onglet). ⚠️ On ne la retire QUE si ce dock existe : sans session il n'est pas
     monté et la croix redevient la seule issue.
     ⚠️ ET seulement en vue LISTE (`:not(.neko-msg-thread)`) : dans un fil, la
     flèche « Retour », l'appel vidéo et le menu « Plus d'options » portent la
     MÊME classe .neko-drawer-close, donc cette règle les effaçait tous les
     trois. On ne pouvait plus revenir à la liste. */
  body.neko-mobile-shell.has-dock:not(.neko-msg-thread) .neko-msg-drawer .neko-drawer-close{ display: none; }
  body.neko-mobile-shell #neko-msg-cta{ display: none; }

  /* --- Recherche (miroir du champ de messages.tsx) --- */
  body.neko-mobile-shell .neko-msg-search{
    display: flex; align-items: center; gap: 8px;
    margin: 0 22px 8px; padding: 10px 14px;
    background: var(--white); border-radius: 999px;
  }
  body.neko-mobile-shell .neko-msg-search svg{ width: 14px; height: 14px; color: var(--muted); flex: 0 0 auto; }
  body.neko-mobile-shell .neko-msg-search input{
    flex: 1; min-width: 0; border: 0; background: transparent; outline: none;
    font-family: var(--sans); font-size: 12px; color: var(--ink);
    -webkit-appearance: none; appearance: none;
  }
  body.neko-mobile-shell .neko-msg-search input::placeholder{ color: var(--muted); }
  body.neko-mobile-shell .neko-msg-search input::-webkit-search-cancel-button{ -webkit-appearance: none; }

  /* --- Item de conversation façon appli (mobile/app/messages.tsx) --- */
  body.neko-mobile-shell .conv-item .time{
    display: block; flex: 0 0 auto; margin-top: 0;
    font-family: var(--mono, monospace); font-size: 10px; color: var(--muted);
    text-transform: uppercase;
  }
  body.neko-mobile-shell .conv-item.is-unread .time{ color: var(--terracotta); }
  body.neko-mobile-shell .conv-item .ann-app{
    display: block; margin-top: 2px; font-size: 10.5px; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* Non-lus : compteur chiffré comme l'appli, plus le simple point du desktop. */
  body.neko-mobile-shell .conv-item .unread{
    width: auto; min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 9px; align-self: center; margin-top: 0;
    display: grid; place-items: center;
    font-family: var(--sans); font-size: 10px; font-weight: 700; color: #fff;
  }
}

/* ===========================================================================
   DOCK DU BAS — miroir de la TabBar de l'appli. Affiché quand le shell est en
   format appli (`body.neko-mobile-shell`, cf. nekoShellModeSync) : ≤1024px,
   téléphone ET tablette, POUR LES DEUX RÔLES — propriétaire depuis le
   2026-08-24, sitter depuis le 2026-08-26 (demande Julie). La media
   (max-width:1024px) n'est qu'une ceinture, la classe fait foi.
   Monté par nekoMountDock() quand connecté. ⚠️ IL EST TOUTE LA NAVIGATION DU
   FORMAT APPLI, DANS LES DEUX RÔLES : le tiroir est masqué (`body.no-burger`,
   owner 2026-07-26, sitter 2026-07-30) et la barre du haut aussi
   (`body.has-tabbar .nav`). C'est la copie conforme de l'appli.
   ⚠️ « SITTER : navigation hybride, le tiroir garde le menu exhaustif » est
   ABROGÉ — c'était déjà faux sur téléphone depuis le 2026-07-30, et ça l'est
   maintenant jusqu'à 1024px. Corollaire à tenir : toute entrée ajoutée à
   NEKO_SITTER_TABS n'existe plus qu'AU-DESSUS de 1025px ; sous ce seuil il faut
   lui donner une porte explicite (un onglet de page, une ligne de
   /profil-sitter), exactement comme côté propriétaire.

   Historique à connaître avant de retoucher : une première barre du bas a
   existé du 2026-07-11 (a19c1b2) au 2026-07-20 (8c27823), retirée non pas
   pour un souci de CSS mais parce que le menu avait grossi à 6 entrées owner
   et 8 sitter, ce qu'une barre du bas ne tient pas. D'où la règle : le dock
   plafonne à 4 entrées, tout ajout va dans le tiroir.
   ⚠️ `.neko-tabbar` désigne le TIROIR (nom hérité de cette première barre),
   pas ce dock. Ne pas confondre les deux en éditant.

   Empilement (z-index) — le dock est volontairement BAS, à 80 :
     4000 lightbox · 200 tiroir · 199 voile du tiroir · 120 hamburger
     · 100 pop-up messagerie/notifs · 90 son voile · [80 dock]
     · 45/40/30 barres d'action des pages
   Au-dessus de 90 il flotterait par-dessus le pop-up messagerie ; au-dessus de
   199 il dépasserait du voile quand le tiroir est ouvert (le tiroir ne fait que
   90% de large, le dock déborderait à droite).
   ========================================================================= */
.neko-dock{ display: none; }

@media (max-width: 1024px){
  body.neko-mobile-shell.has-dock .neko-dock{
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
    display: flex; flex-direction: row; align-items: center; justify-content: space-around;
    height: calc(var(--neko-dock-h) + env(safe-area-inset-bottom, 0px));
    /* ⚠️ Le dock descend jusqu'au bord de l'écran : c'est ce padding qui garde
       les onglets au-dessus du home indicator iOS / de la barre de nav Android,
       exactement comme dans l'appli (TabBar.tsx). Sans `viewport-fit=cover` sur
       le <meta viewport>, `env()` vaut 0 — et c'est CORRECT : iOS insère alors
       lui-même la marge sûre hors du viewport, donc `bottom: 0` est déjà au
       bon endroit. Le jour où on passera en `viewport-fit=cover` (rendu bord à
       bord, verre du dock qui passe sous l'indicateur), cette ligne prend le
       relais toute seule, sans rien changer ici. */
    /* Longhands, et `env()` enveloppé dans un calc() : csstree-validator (lancé
       sur ce fichier, jusqu'ici sans erreur) ne connaît pas env() nu, ni dans un
       raccourci ni seul. Le calc() est la forme déjà employée partout ailleurs
       ici, elle passe le contrôle et vaut exactement la même chose. */
    padding-top: 0; padding-right: 8px; padding-left: 8px;
    padding-bottom: calc(env(safe-area-inset-bottom, 0px));
    border-top-left-radius: 24px; border-top-right-radius: 24px;
    /* Verre dépoli, comme la TabBar de l'appli (BlurView 45 + voile).
       Le voile est plus opaque qu'en natif (.72 contre .30) : le flou CSS ne
       « colle » pas autant que le flou natif, en dessous les libellés
       deviennent illisibles quand une photo défile dessous.
       ⚠️ Le voile est CRÈME, jamais blanc (corrigé le 2026-08-03). Tout le
       verre du site est teinté crème (`.nav` à .88, la lightbox à .42) : à
       72 % d'opacité le flou ne se voit presque plus, il ne reste que
       l'aplat, et un aplat blanc sur une page crème se lit comme une barre
       blanche posée là, pas comme du verre. Le blanc venait de la TabBar
       native, où il est à 30 % et où c'est le flou qui fait le travail. */
    background: rgba(251, 247, 243, 0.72);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    backdrop-filter: blur(18px) saturate(140%);
    /* Ombre du menu burger de la landing, décalages inversés (le dock est collé
       en bas, vers le bas l'ombre tomberait hors écran). Les DEUX couches
       comptent : la serrée pose la barre, la diffuse l'éloigne du fond. */
    box-shadow: 0 -1px 2px rgba(26, 22, 20, .05), 0 -16px 48px rgba(26, 22, 20, .14);
  }
  /* Firefox < 103 et navigateurs sans backdrop-filter : fond plein, sinon le
     dock devient un voile à 72% derrière lequel le texte bave. Crème plein,
     pas blanc : le repli doit ressembler à la version floutée, pas à celle
     qu'on vient de retirer. */
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
    body.neko-mobile-shell.has-dock .neko-dock{ background: var(--cream); }
  }

  /* --- MESSAGERIE OUVERTE, et LÀ SEULEMENT (2026-07-30) -------------------
     Sous la messagerie plein écran, le dock ne surplombe plus une page qui
     défile : il touche la barre de saisie, posée juste au-dessus de lui. Le
     verre dépoli y virait au blanc contre le crème du tiroir (deux teintes
     voisines mais distinctes, sur 2 cm de haut), et l'échancrure des coins
     arrondis laissait reparaître le crème DANS l'angle, assombri par l'ombre :
     deux petits croissants gris aux extrémités.
     On aligne donc le fond sur celui du tiroir et on redresse le haut de la
     barre. ⚠️ L'OMBRE RESTE, ici comme partout : c'est elle qui pose la barre,
     et c'est le seul relief conservé une fois les couleurs égalisées.
     ⚠️ Ne PAS généraliser aux autres écrans : ailleurs le dock flotte au-dessus
     d'un contenu qui défile dessous, et c'est le verre dépoli qui garde les
     libellés lisibles quand une photo passe derrière. */
  body.neko-mobile-shell.neko-msg-open .neko-dock{
    border-top-left-radius: 0; border-top-right-radius: 0;
    background: var(--cream);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }

  /* Réserve la bande du dock en bas de page. Sélecteur à DEUX classes pour
     passer devant `body.has-tabbar{ padding-bottom: 0 }` ci-dessus sans
     dépendre de l'ordre des règles (le dock ne se monte jamais sans le
     tiroir : les deux exigent une session). */
  body.neko-mobile-shell.has-tabbar.has-dock{ padding-bottom: calc(var(--neko-dock-h) + env(safe-area-inset-bottom, 0px)); }

  /* Barres d'action collées en bas des pages : elles remontent au-dessus du dock.
     ⚠️ Toute nouvelle barre collée en bas doit être ajoutée ici, sinon elle
     passe sous le dock (elles vivent dans le <style> de leur page, qui ne
     connaît pas la géométrie du dock).
     Vaut pour `fixed` ET pour `sticky` : le padding bas du body ne protège pas
     un élément sticky, qui se cale sur le bord du scrollport et non sur la boîte
     de contenu du body. */
  body.neko-mobile-shell.has-dock .jg-bar,        /* /journal-garde : photos + appel  (fixed)  */
  body.neko-mobile-shell.has-dock .sign-bottom,   /* /signalement : envoyer           (fixed)  */
  body.neko-mobile-shell.has-dock .cn-bar,        /* /classified-new : publier        (fixed)  */
  body.neko-mobile-shell.has-dock .as-input-bar,  /* /assistant : saisie du chat      (sticky) */
  body.neko-mobile-shell.has-dock .cp-save-bar{   /* /cat-profile : enregistrer       (sticky) */
    bottom: calc(var(--neko-dock-h) + env(safe-area-inset-bottom, 0px));
  }

  /* Même raison que pour le hamburger (cf. body.ap-modal-open .neko-menu-btn) :
     les panneaux .ap-detail de /profil-sitter et /profil-proprietaire vivent
     dans `.wrap`, qui porte z-index:1 et emprisonne leur z-index:200 — ils ne
     peuvent pas passer devant un élément du body. On retire donc le dock
     pendant qu'un panneau est ouvert au lieu de surenchérir. */
  body.neko-mobile-shell.ap-modal-open .neko-dock{ display: none; }

  .neko-dock-tab{
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    padding: 3px 4px;
    background: none; border: 0; cursor: pointer;
    font-family: var(--sans); font-size: 11px; font-weight: 500; letter-spacing: .2px;
    color: var(--muted); text-decoration: none;
    -webkit-tap-highlight-color: transparent;
  }
  /* Libellé de l'onglet. ⚠️ `:not(.neko-dock-ico)` est indispensable : la
     pastille de l'icône est elle aussi un <span> enfant direct, et son
     `overflow: hidden` rognait la pastille de non-lus (ancrée en -2/-5 hors du
     carré de 32px, donc coupée le long du cercle : il n'en restait qu'un
     croissant rouge). Ne pas revenir à un sélecteur `> span` nu. */
  .neko-dock-tab > span:not(.neko-dock-ico){
    line-height: 1; max-width: 100%;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* Pastille corail derrière l'icône de l'onglet actif. Toujours dans l'arbre
     (fond transparent quand inactif) pour que la géométrie du dock ne bouge pas
     d'un onglet à l'autre. */
  .neko-dock-ico{
    position: relative;
    width: 32px; height: 32px; border-radius: 16px;
    display: grid; place-items: center;
    background: transparent;
    transition: background .15s ease;
  }
  .neko-dock-ico svg{ width: 22px; height: 22px; }
  .neko-dock-tab.active{ color: var(--terracotta); font-weight: 700; }
  .neko-dock-tab.active .neko-dock-ico{ background: color-mix(in oklab, var(--terracotta) 12%, transparent); }
  /* Enfoncement au tap, comme les onglets de l'appli. */
  .neko-dock-tab:active{ transform: translateY(3px); }
  @media (prefers-reduced-motion: reduce){ .neko-dock-tab:active{ transform: none; } }

  /* Pastille de non-lus, ancrée sur la pastille de 32px (et non sur l'icône
     de 22px, qui a un repère 5px plus serré). */
  .neko-dock-badge{
    position: absolute; top: -2px; right: -5px;
    min-width: 16px; height: 16px; padding: 0 4px;
    border-radius: 8px;
    background: var(--terracotta); color: #fff;
    border: 1.5px solid var(--white);
    font-family: var(--sans); font-size: 10px; font-weight: 700; line-height: 1;
    display: grid; place-items: center;
  }
}

/* Desktop : le menu vertical devient une sidebar fixe toujours visible. Le
   hamburger et le voile disparaissent ; la barre du haut (.nav) est supprimée
   au profit de cette sidebar.
   ⚠️ LE BLOC EST DÉCLARÉ À ≥768px MAIS NE PREND EFFET QU'À PARTIR DE 1025px
   pour un utilisateur CONNECTÉ, dans les DEUX rôles (propriétaire depuis le
   2026-08-24, sitter depuis le 2026-08-26, demande Julie). Entre 768 et 1024px
   la tablette est en format appli (`body.neko-mobile-shell`, posée par
   nekoShellModeSync) : le shell mobile plus haut masque la sidebar (règle
   `.no-burger`) et remet `padding-left: 0`, et c'est le dock du bas qui navigue.
   ⚠️ « le sitter la garde dès 768px » est ABROGÉ (24 → 26 août) : ne pas le
   restaurer en relisant un texte daté.
   ⚠️ Le 768 de la media N'EST PAS DEVENU DÉCORATIF pour autant : il sert encore
   le visiteur NON connecté (pas de `neko-mobile-shell` sans session) et il est
   le seuil qu'une page recopie quand elle écrit « ici la sidebar est là ». Une
   telle page doit s'écrire `body:not(.neko-mobile-shell)` et non `min-width:
   1025px`, sans quoi elle se décroche le jour où le seuil rebouge. */
@media (min-width: 768px){
  /* ⚠️ 296px, et non les 232 d'origine (2026-08-23, demande Julie : « élargis un
     peu le menu, il est trop compact », puis « élargis encore » — d'où deux
     paliers le même jour, 232 → 264 → 296). Les 232px venaient d'un menu à
     libellés courts ; depuis, il porte deux CTA, un bloc de raccourcis, des
     intertitres de groupe et le pied de compte, et tout y tenait sur des lignes
     serrées. ⚠️ `body.has-tabbar{padding-left}` juste dessous porte la MÊME
     valeur : les deux bougent ensemble, sinon le contenu de la page passe sous
     le menu (il est en `position:fixed`). */
  .neko-tabbar{
    width: 296px; max-width: none;
    padding: 26px 20px 20px;
    transform: none;
    transition: none;
    box-shadow: none;
    z-index: 60;
  }
  .neko-menu-btn, .neko-tabbar-backdrop{ display: none; }
  body.has-tabbar{ padding-left: 296px; }
  /* ═══ ≥1025px, LE MENU DEVIENT UNE CARTE BLANCHE (2026-08-24, demande Julie :
     « des cartes blanches avec des radius pour toute la page », maquette du
     dashboard) : la colonne flotte à 16px des bords, blanche, arrondie, ombrée —
     la boîte EXACTE de la colonne messagerie (`.neko-owner-rail`, plus haut),
     en miroir gauche. Le padding du body suit (296 de carte + 16 de bord + 12
     de gouttière). ⚠️ Média IMBRIQUÉE dans le bloc ≥768px, exprès : déclarée
     APRÈS les règles qu'elle raffine, c'est l'ordre qui la fait gagner — la
     sortir du bloc la ferait passer avant et perdre. Entre 768 et 1024px le
     menu garde sa colonne pleine hauteur beige : il n'y a pas de carte
     messagerie à égaler sur cette plage. */
  @media (min-width: 1025px){
    .neko-tabbar{
      top: 16px; left: 16px; bottom: 16px;
      background: var(--white);
      border: 1px solid var(--line); border-radius: 22px;
      box-shadow: 0 24px 60px -28px rgba(31, 26, 26, .38), 0 3px 10px -6px rgba(31, 26, 26, .14);
    }
    body.has-tabbar{ padding-left: 324px; }
    /* ⚠️ EN MODE CONTEXTUEL, LE MENU CESSE D'ÊTRE UNE CARTE (2026-08-24,
       demande Julie : « Bichon n'est pas dans le menu vertical, il a sa propre
       bulle ») : la colonne redevient transparente et ce sont ses BULLES qui
       flottent sur le beige — la carte sombre du chat (.sctx-card) et le
       panneau blanc de la liste (.sctx-panel), chacune la sienne, comme la
       maquette. Le titre « Le carnet de <chat> » et le rôle sont masqués ICI
       SEULEMENT : la carte sombre porte déjà le nom du chat, les garder les
       ferait lire deux fois l'un au-dessus de l'autre. */
    .neko-tabbar.has-side-ctx{
      background: transparent; border: 0; box-shadow: none;
      padding-left: 6px; padding-right: 6px;
    }
    .neko-tabbar.has-side-ctx .neko-side-title,
    .neko-tabbar.has-side-ctx .neko-side-role{ display: none; }
  }
  /* ⚠️ « Mon compte » RESTE VISIBLE en desktop (2026-07-29). Il était masqué ici
     parce que l'en-tête de la sidebar portait la photo et le prénom, qui menaient
     au profil — or cet en-tête affiche désormais « Le carnet de <chat> » sur le
     dashboard propriétaire. Le masquer laissait donc l'écran sans aucun chemin
     vers les réglages du compte. */
  /* ⚠️ `.neko-side-shortcuts.is-sitter{display:none}` EN DESKTOP A ÉTÉ SUPPRIMÉ
     (2026-08-23). Cette règle retirait de la sidebar le raccourci SITTER « Voir
     mon profil public » au-dessus de 1025px, parce que le rail de droite portait
     le même CTA (`.rail-me-public`). Le rail est remplacé par la messagerie, ce
     jumeau n'existe plus : sans cette suppression, le raccourci disparaissait
     exactement là où plus rien ne le remplaçait.
     ⚠️ Le bloc PROPRIÉTAIRE n'a jamais été concerné : ses deux raccourcis n'ont
     pas de jumeau dans une colonne depuis le 2026-07-29.
     ⚠️ Le rattrapage de filet qui accompagnait la règle avait déjà été retiré le
     2026-07-31 (le trait vit sur `.neko-tab-list` en toutes circonstances) : il
     n'y a rien à remettre en la supprimant. */
  /* Layout « app » : le contenu se cale à GAUCHE (contre la sidebar) au lieu
     d'être centré dans l'espace restant → plus de grand vide entre la sidebar
     et le contenu. Les max-width des .wrap gardent des lignes lisibles.
     ⚠️ `:not(.neko-mobile-shell)` : sans sidebar (tablette en format appli,
     les deux rôles depuis le 2026-08-26), il n'y a rien contre quoi se caler —
     le contenu reste centré, comme en mobile. */
  body.has-tabbar:not(.neko-mobile-shell) .wrap,
  body.has-tabbar:not(.neko-mobile-shell) .wrap-narrow{ margin-left: 0; margin-right: auto; }
}

/* ============================================================
   FOOTER : ABSENT DES PAGES « APP » (dashboards owner et sitter)
   ------------------------------------------------------------
   Décision 2026-07-27 : le grand footer sombre appartient au site
   vitrine. Sur les pages connectées il n'a aucune raison d'être :
   la navigation y passe par la sidebar et le dock, jamais par des
   liens de pied de page.
   ⚠️ Et surtout il ne POUVAIT PAS y être correct. La sidebar
   (296px) et le rail owner (332px) sont `position:fixed` de haut en
   bas et opaques : pour courir sur toute la largeur, le footer
   devait passer AU-DESSUS d'eux (z-index 70) avec des marges
   négatives, et <body> en colonne flex + `margin-top:auto` le
   collaient au bas du VIEWPORT dès que la page était courte. Il
   tranchait alors le rail en deux (carte « Autour de vous » coupée)
   et se lisait comme une barre fixe. On ne le repositionne pas : on
   le retire, et il n'y a plus rien à faire courir sous les colonnes
   fixes.
   ⚠️ `body.has-tabbar` est posée par nekoMountBottomNav() UNIQUEMENT
   quand la session est authentifiée : aucune page vue par un
   visiteur non connecté n'est touchée.

   ⚠️ RETRAIT BORNÉ AU MOBILE (2026-07-27, second passage). La règle
   ci-dessous était globale : elle retirait donc aussi le footer en
   tablette et en desktop, hors du périmètre demandé. Le desktop
   retrouve le footer ET les règles qui le faisaient courir sous les
   colonnes fixes (restaurées telles quelles depuis 03fadbf).

   ⚠️ LE FOOTER NE COURT PLUS SOUS LES COLONNES (2026-07-28). Les
   marges négatives (-296px / -332px) et le `z-index:70` qui les
   accompagnait sont RETIRÉS : ils étaient précisément ce qui faisait
   passer la bande sombre PAR-DESSUS la sidebar et le rail owner, tous
   deux `position:fixed` de haut en bas. Sur une page courte, le
   footer se colle au bas du viewport (`margin-top:auto`, voulu) et
   tranchait alors le rail en deux, la carte « Autour de vous » coupée
   en plein milieu. Un élément en flux normal ne peut pas se glisser
   SOUS un élément fixe et opaque : il ne reste que la solution
   inverse, le garder DANS la colonne de contenu, entre les deux
   gouttières que <body> réserve déjà. Ne pas réintroduire de marge
   négative ni de z-index ici : ça ramène le défaut tel quel.
   ============================================================ */
/* ⚠️ Keyé sur le FORMAT APPLI (`neko-mobile-shell`) et plus sur la seule
   largeur (2026-08-24) : le retrait du footer suit le shell mobile, qui monte
   à 1024px pour les DEUX rôles depuis le 2026-08-26 (le sitter y est passé ce
   jour-là ; « reste à 767px pour le sitter » est abrogé). */
@media (max-width: 1024px){
  body.neko-mobile-shell.has-tabbar #footer-slot,
  body.neko-mobile-shell.has-tabbar .foot{ display: none; }
}
@media (min-width: 768px){
  /* `:not(.neko-mobile-shell)` : en tablette (format appli, les deux rôles) le
     footer est masqué par le bloc ci-dessus — passer quand même le body en
     colonne flex ne servirait à rien et écarterait la mise en page du mobile
     qu'elle copie. */
  body.has-tabbar:not(.neko-mobile-shell){ display: flex; flex-direction: column; }
  /* En colonne flex, un enfant direct portant `margin-right:auto` (règle
     « layout app » plus haut) se dimensionne sur son contenu au lieu de
     s'étirer : on lui rend sa largeur pleine, les `max-width` des .wrap
     continuent de la plafonner. */
  body.has-tabbar:not(.neko-mobile-shell) > .wrap,
  body.has-tabbar:not(.neko-mobile-shell) > .wrap-narrow{ width: 100%; }
  /* Page courte : le footer descend au bas du viewport au lieu de flotter
     juste sous un contenu de trois lignes. */
  body.has-tabbar:not(.neko-mobile-shell) > #footer-slot{ margin-top: auto; width: 100%; }
  /* Le contenu du footer se recentre dans la colonne : calé à gauche par la
     règle « layout app », il collerait au bord. */
  body.has-tabbar:not(.neko-mobile-shell) .foot .wrap{ margin-left: auto; margin-right: auto; }
}

/* ============================================================
   CTA CORAIL — relief 3D « hero », généralisé à TOUT le site
   ------------------------------------------------------------
   Source de vérité unique du look CTA hero (fill corail, liseré
   encre décalé vers le haut via ::before, tranche 3D coralInk
   #C44C3D, s'enfonce au clic). `.btn-primary` / `.nav-cta` sont
   déjà traités plus haut (bloc L439). Ce bloc rattrape tous les
   CTA corail « maison » définis localement dans les pages HTML
   (submit de formulaire, pills « nouvelle annonce », CTA cartes,
   etc.) qui n'avaient qu'un relief partiel ou plat. `!important`
   pour gagner sur les <style> locaux des pages (même logique que
   le bloc canonique de globals.css). NE PAS ajouter ici des
   éléments non-cliquables (badges, dots, chips sélectionnées) —
   uniquement des boutons d'action.
   ============================================================ */
.neko-msg-cta-btn,
.bk-send, .be-btn-primary, .cn-cta, .cp-save, .cg-save, .rs-send,
.cn-publish, .bp-cta, .bp-create-pill, .loc-apply, .ma-new, .md-new,
.mpa-cta, .occ-create, .occ-contact, .rf-invite-btn, .ap-cta,
.pf-public-link, .odash-cta-card.is-coral, .conv-reserve, .sp-cta,
.sp-bank-add, .mp-btn.primary, .sp-idcard-cta, .vet-btn-fill,
.msg-assistant-btn, .ss-current-card, .ap-becomesitter,
.jg-publish, .jrep-add,
#wiz-next, #wiz-submit {
  position: relative !important;
  border: 1px solid transparent !important;
  box-shadow: 0 1px 0 0 #C44C3D !important;
  transition: transform .1s ease, box-shadow .1s ease, background .2s, color .2s !important;
  will-change: transform;
}
.neko-msg-cta-btn::before,
.bk-send::before, .be-btn-primary::before, .cn-cta::before, .cp-save::before,
.cg-save::before, .rs-send::before, .cn-publish::before, .bp-cta::before,
.bp-create-pill::before, .loc-apply::before, .ma-new::before, .md-new::before,
.mpa-cta::before, .occ-create::before, .occ-contact::before, .rf-invite-btn::before,
.ap-cta::before, .pf-public-link::before, .odash-cta-card.is-coral::before,
.conv-reserve::before, .sp-cta::before, .sp-bank-add::before, .mp-btn.primary::before,
.sp-idcard-cta::before, .vet-btn-fill::before, .msg-assistant-btn::before,
.ss-current-card::before, .ap-becomesitter::before,
.jg-publish::before, .jrep-add::before,
#wiz-next::before, #wiz-submit::before {
  content: "" !important;
  position: absolute !important;
  inset: 0 !important;
  border: 1px solid #1F1A1A !important;
  border-radius: inherit !important;
  transform: translateY(-3px) !important;
  pointer-events: none !important;
}
.neko-msg-cta-btn:hover,
.bk-send:hover, .be-btn-primary:hover, .cn-cta:hover, .cp-save:hover, .cg-save:hover,
.rs-send:hover, .cn-publish:hover, .bp-cta:hover, .bp-create-pill:hover, .loc-apply:hover,
.ma-new:hover, .md-new:hover, .mpa-cta:hover, .occ-create:hover, .occ-contact:hover,
.rf-invite-btn:hover, .ap-cta:hover, .pf-public-link:hover, .odash-cta-card.is-coral:hover,
.conv-reserve:hover, .sp-cta:hover, .sp-bank-add:hover, .mp-btn.primary:hover,
.sp-idcard-cta:hover, .vet-btn-fill:hover, .msg-assistant-btn:hover, .ss-current-card:hover,
.ap-becomesitter:hover, .jg-publish:hover, .jrep-add:hover,
#wiz-next:hover, #wiz-submit:hover {
  transform: translateY(-1px) !important;
  box-shadow: 0 2px 0 0 #C44C3D !important;
}
.neko-msg-cta-btn:active,
.bk-send:active, .be-btn-primary:active, .cn-cta:active, .cp-save:active, .cg-save:active,
.rs-send:active, .cn-publish:active, .bp-cta:active, .bp-create-pill:active, .loc-apply:active,
.ma-new:active, .md-new:active, .mpa-cta:active, .occ-create:active, .occ-contact:active,
.rf-invite-btn:active, .ap-cta:active, .pf-public-link:active, .odash-cta-card.is-coral:active,
.conv-reserve:active, .sp-cta:active, .sp-bank-add:active, .mp-btn.primary:active,
.sp-idcard-cta:active, .vet-btn-fill:active, .msg-assistant-btn:active, .ss-current-card:active,
.ap-becomesitter:active, .jg-publish:active, .jrep-add:active,
#wiz-next:active, #wiz-submit:active {
  transform: translateY(1px) !important;
  box-shadow: 0 0 0 0 #C44C3D !important;
}
@media (prefers-reduced-motion: reduce){
  .neko-msg-cta-btn, .bk-send, .be-btn-primary, .cn-cta, .cp-save, .cg-save, .rs-send,
  .cn-publish, .bp-cta, .bp-create-pill, .loc-apply, .ma-new, .md-new, .mpa-cta,
  .occ-create, .occ-contact, .rf-invite-btn, .ap-cta, .pf-public-link,
  .odash-cta-card.is-coral, .conv-reserve, .sp-cta, .sp-bank-add, .mp-btn.primary,
  .sp-idcard-cta, .vet-btn-fill, .msg-assistant-btn, .ss-current-card, .ap-becomesitter,
  .jg-publish, .jrep-add,
  #wiz-next, #wiz-submit {
    transition: background .2s, color .2s !important;
  }
  .neko-msg-cta-btn:hover, .neko-msg-cta-btn:active,
  .bk-send:hover, .bk-send:active, .be-btn-primary:hover, .be-btn-primary:active,
  .cn-cta:hover, .cn-cta:active, .cp-save:hover, .cp-save:active, .cg-save:hover, .cg-save:active,
  .rs-send:hover, .rs-send:active, .cn-publish:hover, .cn-publish:active,
  .bp-cta:hover, .bp-cta:active, .bp-create-pill:hover, .bp-create-pill:active,
  .loc-apply:hover, .loc-apply:active, .ma-new:hover, .ma-new:active, .md-new:hover, .md-new:active,
  .mpa-cta:hover, .mpa-cta:active, .occ-create:hover, .occ-create:active,
  .occ-contact:hover, .occ-contact:active, .rf-invite-btn:hover, .rf-invite-btn:active,
  .ap-cta:hover, .ap-cta:active, .pf-public-link:hover, .pf-public-link:active,
  .odash-cta-card.is-coral:hover, .odash-cta-card.is-coral:active,
  .conv-reserve:hover, .conv-reserve:active, .sp-cta:hover, .sp-cta:active,
  .sp-bank-add:hover, .sp-bank-add:active, .mp-btn.primary:hover, .mp-btn.primary:active,
  .sp-idcard-cta:hover, .sp-idcard-cta:active, .vet-btn-fill:hover, .vet-btn-fill:active,
  .msg-assistant-btn:hover, .msg-assistant-btn:active, .ss-current-card:hover, .ss-current-card:active,
  .ap-becomesitter:hover, .ap-becomesitter:active,
  .jrep-add:hover, .jrep-add:active,
  #wiz-next:hover, #wiz-next:active, #wiz-submit:hover, #wiz-submit:active {
    transform: none !important;
  }
}

/* ============ Lightbox photos (journal de garde, owner + sitter) ============
   Visionneuse plein écran partagée : ouverte par nekoOpenLightbox() dans
   shared.js. Les images cliquables portent la classe .neko-zoomable. */
.neko-zoomable{cursor:zoom-in}
/* ⚠️ VERRE DÉPOLI CRÈME, PLUS LE NOIR PLEIN (2026-07-30). Le noir à 92 %
   coupait la photo de la page et donnait un ton funèbre à ce qui est, la
   plupart du temps, une photo de chat envoyée par son sitter. Le fond garde
   maintenant la page derrière, très floutée et à peine teintée de crème : on
   voit d'où l'on vient, l'ouverture se lit comme un agrandissement et non
   comme un changement d'écran.
   ⚠️ LE CLAIR SE PAIE SUR LES CONTRÔLES, et c'est non négociable : la croix,
   les flèches et le compteur étaient BLANCS sur un voile noir. Les laisser tels
   quels sur du crème les rendait invisibles. Ils passent donc en encre sur
   pastille blanche — un fond clair impose des commandes sombres, on ne peut pas
   changer l'un sans l'autre.
   ⚠️ Le voile crème est faible (42 %) parce que c'est le FLOU qui sépare, pas
   l'opacité. Ne pas le monter pour « mieux détacher » : on retomberait sur un
   aplat, et tout l'intérêt est qu'on reconnaisse la page dessous.
   ⚠️ `@supports` obligatoire : sans `backdrop-filter`, il n'y a plus de flou du
   tout, donc plus rien qui sépare — un voile à 42 % laisserait la page NETTE et
   parfaitement lisible derrière la photo. Le repli monte donc à 96 % de crème :
   moins joli, jamais cassé.
   ⚠️ Composant PARTAGÉ (journal de garde, côté propriétaire ET côté sitter) :
   ce qu'on change ici se voit partout où une image est cliquable. */
.neko-lb{position:fixed; inset:0; z-index:4000; background:rgba(251,247,243,.96); display:none; align-items:center; justify-content:center; overscroll-behavior:contain}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .neko-lb{background:rgba(251,247,243,.42);
    -webkit-backdrop-filter:blur(34px) saturate(1.2);
    backdrop-filter:blur(34px) saturate(1.2)}
}
.neko-lb.open{display:flex}
/* Ombre chaude et portée : sur un fond clair et flou, une photo sans ombre
   flotte sans se poser. Plus de liseré blanc — il servait contre le noir, il
   disparaît sur du crème. */
.neko-lb-img{max-width:92vw; max-height:86vh; object-fit:contain; border-radius:10px; box-shadow:0 24px 60px -18px rgba(60,40,30,.45), 0 2px 10px -2px rgba(60,40,30,.12)}
/* Pastilles claires à commande sombre : lisibles sur le crème flouté comme
   au-dessus d'une photo claire, sans jamais peser sur l'image. */
.neko-lb-close{position:absolute; top:16px; right:18px; width:40px; height:40px; border:none; border-radius:999px; background:rgba(255,255,255,.82); color:var(--ink, #1F1A1A); font-size:22px; line-height:1; cursor:pointer; box-shadow:0 2px 10px -2px rgba(60,40,30,.22)}
.neko-lb-nav{position:absolute; top:50%; transform:translateY(-50%); width:46px; height:46px; border:none; border-radius:999px; background:rgba(255,255,255,.82); color:var(--ink, #1F1A1A); font-size:24px; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center; box-shadow:0 2px 10px -2px rgba(60,40,30,.22)}
/* Télécharger la photo ouverte (2026-09-01) : à GAUCHE de la croix, même
   pastille, même poids. Ce n'est pas une action secondaire, c'est ce que le
   propriétaire vient chercher dans le journal de son séjour. */
.neko-lb-dl{position:absolute; top:16px; right:66px; width:40px; height:40px; border:none; border-radius:999px; background:rgba(255,255,255,.82); color:var(--ink, #1F1A1A); cursor:pointer; display:flex; align-items:center; justify-content:center; box-shadow:0 2px 10px -2px rgba(60,40,30,.22)}
.neko-lb-dl svg{width:18px; height:18px}
.neko-lb-dl:disabled{opacity:.5; cursor:default}
.neko-lb-close:hover, .neko-lb-nav:hover, .neko-lb-dl:hover{background:#fff}
.neko-lb-prev{left:14px}
.neko-lb-next{right:14px}
.neko-lb-count{position:absolute; bottom:20px; left:50%; transform:translateX(-50%); color:var(--ink, #1F1A1A); font-family:var(--mono); font-size:12px; letter-spacing:.06em; background:rgba(255,255,255,.82); padding:5px 12px; border-radius:999px; box-shadow:0 2px 10px -2px rgba(60,40,30,.22)}
@media (max-width:600px){ .neko-lb-nav{width:40px; height:40px; font-size:20px} }
/* ---- La réaction, posée SUR la photo ouverte (2026-08-31, demande Julie) ----
   Rendue par `nekoOpenLightbox` quand la page nomme la journée (`opts.reactDay`),
   et par personne d'autre : la visionneuse sert aussi des photos qui n'ont pas de
   journée, d'où le `:empty` qui lui retire sa place plutôt qu'un conteneur vide
   qui garderait son coin.
   ⚠️ EN BAS À DROITE et pas au centre : le compteur « 2 / 6 » occupe déjà le
   bas-centre, et deux pastilles superposées au même point sont illisibles.
   ⚠️ Fond blanc opaque et ombre portée comme les autres commandes (croix,
   flèches) : le bouton se pose aussi bien sur le crème flouté que sur une photo
   claire, et il ne pèse sur aucune des deux. */
.neko-lb-react{position:absolute; right:18px; bottom:18px; z-index:1; display:flex; align-items:center}
.neko-lb-react:empty{display:none}
.neko-lb-react .jr-row{margin-top:0; justify-content:flex-end}
.neko-lb-react .jr-add{width:40px; height:40px; background:rgba(255,255,255,.92);
  box-shadow:0 2px 10px -2px rgba(60,40,30,.22)}
.neko-lb-react .jr-add svg{width:20px; height:20px}
.neko-lb-react .jr-add:hover{background:#fff}
.neko-lb-react .jr-pill{background:rgba(255,255,255,.92); box-shadow:0 2px 10px -2px rgba(60,40,30,.22)}

/* ============ Écho d'adresse email (ADR 0034) ============
   Bloc réutilisable posé partout où le parcours bascule vers la boîte mail :
   l'adresse qu'on vient de saisir, réaffichée en évidence, avec un chemin de
   correction quand la correction est encore possible.

   ⚠️ Ce bloc remplace le champ « confirmez votre adresse », écarté par
   l'ADR 0034 : on fait RELIRE, on ne fait pas retaper. Retaper se fait en
   pilote automatique et la faute se recopie ; relire attrape l'erreur de
   croyance (vieille adresse, domaine mal mémorisé), qui est celle que rien
   d'autre n'attrape.

   ⚠️ L'adresse est affichée GRANDE, pas en petit sous le texte : tout l'intérêt
   du dispositif tient à ce qu'on ne puisse pas ne pas la voir. La réduire pour
   des raisons de maquette la rend inutile autant que la supprimer.

   ⚠️ N'affiche JAMAIS que ce que la personne vient de taper. Ne rien y déduire
   du compte (« votre compte », « adresse déjà connue ») : ces écrans sont
   volontairement indifférenciés, cf. ADR 0029. */
.neko-email-echo{margin:14px 0 12px; padding:14px 16px; border-radius:10px;
  background:var(--cream, #FBF6F0); border:1px solid var(--line, #EADFD6); text-align:center}
.neko-email-echo-label{display:block; font-family:var(--mono, ui-monospace, monospace);
  font-size:10.5px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted, #6B5A52); margin:0 0 6px}
/* `break-word` et pas `break-all` : on coupe une adresse longue sur mobile sans
   hacher celles qui tiennent, une adresse tronquée au milieu ne se relit pas. */
.neko-email-echo-addr{display:block; font-size:17px; line-height:1.35; font-weight:600;
  color:var(--ink, #1F1A1A); overflow-wrap:break-word; word-break:break-word}
.neko-email-echo-fix{display:inline-block; margin:10px 0 0; padding:2px;
  background:none; border:0; min-height:0; font-family:inherit; font-size:13px;
  color:var(--terracotta, #FF6B5A); text-decoration:underline; text-underline-offset:3px;
  cursor:pointer}
.neko-email-echo-fix:hover{opacity:.75}
/* Variante des écrans où l'adresse est FIGÉE (annonce publiée, compte ouvert) :
   l'email est la clé de jointure du lazy-signup et l'identifiant de connexion,
   `PATCH /store/owners/me` refuse de le changer. Proposer « corriger » y serait
   un mensonge, on ouvre donc la seule sortie réelle, qui est humaine. */
.neko-email-echo-note{margin:10px 0 0; font-size:12.5px; line-height:1.5;
  color:var(--muted, #6B5A52)}
.neko-email-echo-note a{color:var(--terracotta, #FF6B5A); text-decoration:underline;
  text-underline-offset:2px}
@media (max-width:480px){ .neko-email-echo-addr{font-size:15.5px} }

/* ============================================================================
   LA CARTE D'UNE JOURNÉE DE JOURNAL (déplacée ici le 2026-08-09)
   Montée par DEUX pages, et c'est tout l'objet du déplacement : /mon-espace
   (le propriétaire) et /journal-garde (le sitter, qui lit désormais EXACTEMENT
   la même carte que lui, cf. site/CLAUDE.md). Écrite deux fois, elle aurait
   divergé — et une carte qui diverge fait mentir la seule promesse qu'elle
   porte : « voilà ce que l'autre voit ».
   ⚠️ Le FEUILLETAGE (`.jd-track`, `.jd-nav`, `.jd-dot`) a rejoint ce fichier le
   2026-08-10 : le sitter feuillette lui aussi, exactement comme le propriétaire.
   Ce commentaire a dit « ne PAS y remonter le feuilletage, le journal du sitter
   empile ses journées » du 2026-08-09 au 2026-08-10 : c'est ABROGÉ.
   ⚠️ Ce qui reste propre à /mon-espace est le FORMAT DESKTOP (`.jdd-*`, bande de
   journées + panneau), que le journal du sitter n'a pas.
   ============================================================================ */
/* ⚠️ HALO CHAUD AUTOUR DE LA CARTE (2026-07-30). La carte passe PAR-DESSUS
   l'en-tête épinglé : son bord n'est pas une bordure de carte, c'est la
   frontière entre deux plans qui glissent l'un sur l'autre, et une ombre portée
   seule (vers le bas) la laissait trancher net sur les onglets, surtout en
   haut, là où il n'y avait aucune ombre du tout.
   Trois couches, de la plus large à la plus serrée :
   · un halo corail très dilué et sans décalage, qui déborde de tous les côtés
     et fait lire la démarcation comme de la lumière plutôt que comme une coupe ;
   · un contact sombre et court, qui empêche le halo de faire flotter la carte
     dans le vide ;
   · l'ombre portée d'origine, conservée telle quelle pour la profondeur.
   ⚠️ Corail (`--terracotta`) et jamais gris : sur un fond crème, un halo neutre
   grise l'image et la fait paraître sale. ⚠️ Rester très en dessous de la barre
   du visible en opacité — un halo qu'on remarque devient un cadre lumineux, et
   c'est la photo du chat qui doit attirer l'œil, pas son contour. */
.jd-card{background:var(--white); border-radius:22px; overflow:hidden;
  box-shadow:
    0 0 38px -4px rgba(255,107,90,.15),
    0 2px 10px -2px rgba(60,40,30,.07),
    0 20px 44px -30px rgba(60,40,30,.5)}
/* ============================================================================
   CADRE DES PHOTOS : 4/3 EN MOBILE, CARRÉ EN DESKTOP (2026-07-30)
   ⚠️ Les deux formats ne portent PAS le même cadre, et c'est voulu. Le mobile
   affiche UNE photo qui occupe toute la largeur de l'écran : le moindre gain de
   hauteur y repousse la date et les soins hors de l'écran, alors qu'ils font
   partie de la même journée. Le desktop les affiche en vignettes de 240 à 280px
   dans une grille, où la hauteur ne coûte presque rien et où le carré tient
   mieux le rythme de la bande (`.jdd-day-ph`, plus bas).
   ⚠️ Le 4/3 du mobile a été mis en cause puis rétabli le même jour : c'est un
   cadre PAYSAGE rempli en `object-fit:cover`, donc une photo de téléphone —
   verticale neuf fois sur dix — y perd des bandes en haut et en bas. Le portrait
   (3/4) et le carré (1/1) ont été essayés : les deux rendent l'image trop haute
   sur un écran de téléphone. NE PAS refaire le tour, la coupe est le prix payé
   pour garder la journée entière visible d'un coup d'œil.
   ⚠️ Le bon endroit pour régler le recadrage n'est de toute façon pas ici, c'est
   à l'ENVOI, en laissant le sitter choisir la zone gardée. Tant que ça n'existe
   pas, la photo est rognée au centre — compromis assumé, pas un oubli.
   ⚠️ Et jamais `contain` : plus rien ne serait coupé, mais chaque photo
   porterait deux bandes vides, et un journal de cadres à moitié vides est pire
   qu'un cadrage serré.
   ⚠️ Pas de media query : `.jd-mobile` et `.jdd` sont déjà exclusifs (bascule à
   1024px), donc chaque règle ne s'applique que là où son format est visible.
   ⚠️ PLAFOND EN HAUTEUR D'ÉCRAN (2026-08-04). Le 4/3 seul est une proportion,
   pas une hauteur : sur une fenêtre large et courte (à 593px de large, la photo
   seule fait 420px), la carte dépassait l'écran, et le centrage de
   `jdCenterTodayCard()` perdait son objet — on centrait une carte qu'on ne
   voyait toujours pas en entier. Le cadre est donc borné à ~44 % de la hauteur
   d'écran, le 4/3 restant la forme naturelle quand la place suffit. La coupe
   supplémentaire est le même prix, déjà assumé ci-dessus (`object-fit:cover`
   absorbe la différence) ; le plancher de 220px évite un cadre écrasé sur une
   fenêtre minuscule. `vh` d'abord, `dvh` ensuite, comme pour `.jrnl-hint` :
   les navigateurs qui ignorent `dvh` gardent une hauteur juste. */
.jd-hero{position:relative; aspect-ratio:4/3; background:var(--cream-2);
  max-height:max(220px, 44vh)}
.jd-hero{max-height:max(220px, 44dvh)}
.jd-hero img{width:100%; height:100%; object-fit:cover; cursor:zoom-in}
.jd-hero-tag{position:absolute; left:14px; bottom:14px; padding:5px 11px; border-radius:999px;
  background:rgba(251,247,243,.92); font-family:var(--mono); font-size:9.5px;
  letter-spacing:.06em; color:var(--ink-soft)}
/* « souvenir » : photo que le sitter a prise plus tôt et partagée depuis sa
   galerie (FR56). ⚠️ En FACE de « photo du jour », jamais à sa place : les deux
   disent des choses différentes, l'une que le sitter l'a mise en avant, l'autre
   qu'elle n'a pas été prise pendant le passage. Fond sombre pour ne pas se lire
   comme une seconde pastille de la même famille. */
/* ---------- La journée comme PREUVE DE PASSAGE (2026-08-29) ----------
   La carte de journée ne raconte plus seulement ce que le sitter a publié : elle
   ATTESTE. La photo en pleine largeur porte sa légende (« prise dans l'app ·
   18:52 · non modifiable »), la plaque sombre le jour et les heures relevées, et
   le bloc vert dit ce que Neko garantit — et rien de plus.

   ⚠️ **Ces blocs ne s'affichent QUE si la donnée les autorise.** « Passage
   vérifié par Neko » sur un check-in `suspicious` serait un mensonge, et une
   durée déduite d'un départ manquant serait une estimation présentée comme un
   relevé. Cf. `jdProofRows`. */
.jd-shot{position:relative; margin:0 -16px}
.jd-shot img{width:100%; display:block; aspect-ratio:3/4; object-fit:cover}
/* Bandeau de légende SUR la photo : la mention doit être indissociable de
   l'image qu'elle atteste. Séparée, elle se lirait comme une note de bas de
   page qu'on ne relie plus à rien. */
.jd-shot-cap{position:absolute; left:0; right:0; bottom:0; display:flex; align-items:center; gap:7px;
  padding:22px 18px 12px; font-family:var(--mono); font-size:10.5px; letter-spacing:.04em;
  color:rgba(255,255,255,.94);
  background:linear-gradient(to top, rgba(31,26,23,.72), rgba(31,26,23,0))}
.jd-shot-cap i{width:6px; height:6px; border-radius:50%; background:#fff; flex:0 0 auto}
/* Plaque sombre qui chevauche le bas de la photo. Le chevauchement n'est pas un
   effet : il attache la date et les heures à CETTE image. */
.jd-plate{position:relative; margin:-34px 4px 0; background:var(--ink); color:var(--cream);
  border-radius:16px; padding:15px 18px 16px}
.jd-plate-h{display:flex; align-items:baseline; justify-content:space-between; gap:12px}
.jd-plate-h b{font-family:var(--serif); font-weight:500; font-size:19px; color:var(--cream)}
.jd-plate-h span{font-family:var(--mono); font-size:10px; letter-spacing:.08em; color:rgba(250,245,241,.5)}
.jd-plate-hours{margin-top:8px; font-family:var(--mono); font-size:12px; letter-spacing:.02em;
  color:var(--terracotta)}
/* Ce que Neko garantit, en vert : la seule affirmation de la carte. */
/* ⚠️ DEUX verts, et l'un ne remplace pas l'autre (2026-08-30) : `--vouch-bg`
   remplit L'ENCADRÉ de la preuve, `--vouch-veil` teinte le VOILE de la plaque du
   hero (`#garde-hero-slot .jd-plate`, et son miroir dans l'app), posé à 55-72 %
   PAR-DESSUS une photo. Ne pas « réunifier » les deux : au vert de l'encadré, le
   voile cesserait d'être du verre pour devenir un aplat sur la photo.
   ⚠️ **L'ENCADRÉ A TRAVERSÉ TROIS ÉTATS LE 2026-08-30, dans cet ordre — le
   dernier seul fait foi** (ne pas en restaurer un en relisant un texte daté) :
     1. #EFF5EF / #D6E4D7 — « c'est toujours beige !!! » : sur une page crème, un
        vert à +6 de vert sur le rouge ne se distingue de rien.
     2. #D2E2D5 / #AECBB4 — « je veux que ce soit plus visible » : visible, oui,
        mais « le fond est trop grisâtre ». Un vert désaturé qu'on FONCE vire au
        gris ; ce qui manquait n'était pas de l'obscurité, c'était du VERT.
     3. #E7F4EA / #C8E1CE — pâle comme l'état 1, mais franchement vert (+13 de
        vert sur le rouge, contre +6). C'est la SATURATION qui le détache du
        crème, pas la valeur.
   ⚠️ **LE TEXTE EST VERT, LE TITRE NON** (demande Julie, 2026-08-30 : « le texte
   doit être vert sauf le titre ») : `--vouch-ink` porte le corps et le lien, le
   titre garde `--ink`. Il est plus foncé que `--vouch-green` EXPRÈS — à #4C8C5A
   le corps de 12,5px tombe à 3,6:1 sur ce fond, sous le seuil AA ; #34704A tient
   5,2:1. Ne pas « unifier » ces deux verts-là non plus.
   ⚠️ Le voile s'écrit en `rgba()` dans les deux clients (il lui faut son alpha) :
   ces copies-là bougent AVEC `--vouch-veil`, jamais sans elle. L'encadré, lui, est
   recopié en dur dans `mobile/src/components/{JournalDayCard,PassagesSheet}.tsx` :
   ces valeurs-là bougent ensemble. */
:root{--vouch-bg:#E7F4EA; --vouch-line:#C8E1CE; --vouch-green:#4C8C5A;
  --vouch-ink:#34704A; --vouch-veil:#EFF5EF}
.jd-vouch{display:flex; gap:11px; margin-top:16px; padding:14px 15px; border-radius:14px;
  background:var(--vouch-bg); border:1px solid var(--vouch-line)}
.jd-vouch-ic{flex:0 0 auto; width:20px; height:20px; border-radius:50%; background:#4C8C5A;
  display:grid; place-items:center; color:#fff; font-size:11px; font-weight:700}
.jd-vouch b{display:block; font-family:var(--serif); font-weight:500; font-size:14.5px; color:var(--ink)}
.jd-vouch p{margin:4px 0 0; font-size:12.5px; line-height:1.5; color:var(--vouch-ink)}
/* Variante « on ne peut pas l'affirmer » (« Passage enregistré », pastille `i`).
   ⚠️ **ELLE EST VERTE ELLE AUSSI depuis le 2026-08-30** (demande Julie, deux
   fois : « c'est toujours beige !!! »). Le beige `--cream-2` était le ton neutre
   du doute — sur une page crème il ne se distinguait de rien, et c'est la
   variante que le propriétaire voit le plus souvent (un GPS qui s'écarte suffit
   à la déclencher). ⚠️ Ce qui porte le doute reste donc la PASTILLE GRISE et le
   TEXTE (« la position relevée ne correspond pas à l'adresse ») : ne jamais leur
   donner le ✓ ni le vert de `--vouch-green`, ce serait certifier par la couleur
   ce que la phrase refuse de certifier. */
.jd-vouch.is-doubt{background:var(--vouch-bg); border-color:var(--vouch-line)}
.jd-vouch.is-doubt .jd-vouch-ic{background:var(--muted)}
/* ⚠️ **LE TEXTE DU DOUTE EST VERT FONCÉ LUI AUSSI** (demande Julie, 2026-08-30 :
   « ce texte doit être en vert foncé », capture de la variante du doute). Il a
   vécu quelques heures en `--ink-soft` le même jour, par prudence : c'est ABROGÉ,
   les deux variantes partagent désormais `--vouch-ink`.
   ⚠️ Ce qui certifie reste la PASTILLE (✓ vert contre `i` gris) et la PHRASE.
   `--vouch-ink` est l'encre de l'encadré, pas un label de vérification : sur un
   fond vert, un corps de texte encre-gris se lisait comme posé par erreur. Ne pas
   confondre avec `--vouch-green`, qui LUI certifie — il ne touche jamais la
   variante du doute (cf. le pavé ci-dessus). */
/* ---------- HISTORIQUE DES PASSAGES (2026-08-30, demande Julie) ----------
   « il faudrait remplacer ça par un historique des passages avec le nombre de
   minutes passé chez le proprio ». La ligne précédente portait un rond de
   remplissage (`.jd-pass-av`, un dégradé haché qui ne représentait personne), le
   nom du sitter, et pour tout relevé « durée demandée 10 min · réalisée 15 min »
   — deux durées dans la même petite ligne grise, dont la SEULE qui soit une
   mesure arrivait en second. Sur un passage encore ouvert il ne restait que la
   durée DEMANDÉE : la ligne annonçait alors un chiffre que personne n'avait
   relevé.
   Trois colonnes maintenant : le RANG du passage, ce qu'il est et quand, puis le
   temps réellement passé sur place — seul chiffre qui réponde à « combien de
   temps est-il resté chez moi », donc seul chiffre à droite, en gros.
   ⚠️ `.jd-pass-av` et `.jd-pass-ok` sont SUPPRIMÉS : les trois clients qui
   composaient ce bloc (mon-espace, journal-garde, `JournalDayCard.tsx`) ont
   changé ensemble. Ne pas réintroduire l'un sans les autres. */
.jd-passlog{margin-top:16px}
.jd-passlog-h{display:block; font-family:var(--mono); font-size:9.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--muted)}
.jd-passlog .jd-pass{margin-top:8px}
.jd-pass{display:flex; align-items:center; gap:12px; margin-top:10px; padding:12px 14px;
  background:var(--white); border:1px solid var(--line); border-radius:14px}
/* Le rang, pas un avatar : deux passages dans la journée se distinguent par leur
   ORDRE, et le visage du sitter est déjà partout ailleurs sur la page. */
.jd-pass-n{flex:0 0 auto; width:22px; height:22px; border-radius:7px; display:grid;
  place-items:center; background:var(--cream-2); color:var(--muted);
  font-family:var(--mono); font-size:10.5px}
.jd-pass-main{flex:1; min-width:0}
.jd-pass-main b{display:block; font-size:14px; font-weight:600; color:var(--ink)}
.jd-pass-main small{display:block; margin-top:2px; font-family:var(--mono); font-size:11px; color:var(--muted)}
/* ⚠️ LE VERT NE SE POSE QUE SUR UN PASSAGE VÉRIFIÉ (`.is-ok`) : ailleurs le
   chiffre est exact mais rien ne l'atteste, et la couleur certifierait à la place
   de la phrase. Même règle que la pastille de l'encadré.
   ⚠️ « sur place » n'est pas décoratif : sans lui, un « 15 min » posé à droite se
   lit comme la durée DEMANDÉE, qui figure justement sur la ligne d'à côté. */
.jd-pass-min{flex:0 0 auto; text-align:right; white-space:nowrap}
.jd-pass-min b{display:block; font-family:var(--mono); font-size:13.5px; color:var(--ink)}
.jd-pass-min i{display:block; margin-top:1px; font-style:normal; font-family:var(--mono);
  font-size:9.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted)}
.jd-pass-min.is-ok b{color:var(--vouch-ink)}
/* ⚠️ Le lien vit DANS l'encadré vert (demande Julie, 2026-08-30), sous la
   phrase qu'il prolonge. Posé en pied de carte et centré, il se lisait comme une
   mention de bas de page rattachée à rien : c'est l'affirmation « Passage
   vérifié par Neko » qu'il explique, sa place est à côté d'elle. */
.jd-how{display:inline-block; margin-top:8px; font-size:12px; color:var(--vouch-ink);
  text-decoration:underline; text-underline-offset:2px}
.jd-how:hover{color:var(--ink)}
/* ====== Feuille « Ce que Neko contrôle pendant la garde » (2026-08-30) ======
   Ce que `.jd-how` ouvre désormais (mécanique : shared.js). ⚠️ **FORMAT
   TÉLÉPHONE PARTOUT** (demande Julie) : la boîte est plafonnée à 420px et centrée,
   y compris sur un grand écran. C'est une feuille qu'on lit et qu'on referme,
   pas une page — l'étalée en pleine largeur était justement le défaut de
   /securite, où ce lien menait.
   ⚠️ Le corps défile, la tête et le bouton ne bougent pas : « J'ai compris »
   doit rester atteignable sans avoir lu jusqu'en bas. */
.neko-ps{position:fixed; inset:0; z-index:3900; display:none; align-items:center;
  justify-content:center; padding:20px; background:rgba(31,26,26,.42); overscroll-behavior:contain}
.neko-ps.open{display:flex}
.neko-ps-box{display:flex; flex-direction:column; width:min(420px,100%); max-height:min(92vh,720px);
  background:var(--cream); border-radius:24px; overflow:hidden;
  box-shadow:0 24px 70px -18px rgba(31,26,23,.45)}
.neko-ps-head{display:flex; align-items:flex-start; gap:12px; padding:22px 22px 14px}
.neko-ps-head h2{margin:0; flex:1; font-family:var(--serif); font-weight:600; font-size:22px;
  line-height:1.22; letter-spacing:-.01em; color:var(--ink)}
.neko-ps-x{flex:0 0 auto; width:30px; height:30px; border:0; border-radius:50%; cursor:pointer;
  background:var(--cream-2); color:var(--ink-soft); font-size:19px; line-height:1}
.neko-ps-body{flex:1; min-height:0; overflow-y:auto; padding:0 18px 4px; -webkit-overflow-scrolling:touch}
.neko-ps-card{display:flex; gap:12px; margin-bottom:10px; padding:15px 16px; border-radius:16px;
  background:var(--white); border:1px solid var(--line)}
.neko-ps-n{flex:0 0 auto; width:24px; height:24px; border-radius:8px; background:var(--ink);
  color:var(--cream); display:grid; place-items:center; font-size:12px; font-weight:700}
.neko-ps-card b{display:block; font-family:var(--serif); font-weight:600; font-size:15px; color:var(--ink)}
.neko-ps-card p{margin:5px 0 0; font-size:13px; line-height:1.55; color:var(--ink-soft)}
/* Le relevé porte le vert de la preuve, pas le crème : c'est la seule chose de
   la feuille qui soit une MESURE, le reste est une explication. */
.neko-ps-stat{margin:4px 0 8px; padding:14px 16px; border-radius:16px;
  background:var(--vouch-bg); border:1px solid var(--vouch-line)}
.neko-ps-stat b{display:block; font-size:12.5px; font-weight:700; color:var(--ink)}
.neko-ps-stat p{margin:5px 0 0; font-size:13px; line-height:1.5; color:var(--ink-soft)}
.neko-ps-foot{padding:12px 18px calc(16px + env(safe-area-inset-bottom));
  background:var(--cream); border-top:1px solid var(--line)}
.neko-ps-ok{width:100%; padding:15px; border:0; border-radius:14px; cursor:pointer;
  background:var(--ink); color:var(--cream); font-size:15px; font-weight:600; font-family:inherit}
@media (max-width:560px){
  /* Sur téléphone elle monte du bas et ne s'arrondit qu'en haut : collée au bord
     bas, deux coins ronds y laisseraient deux triangles de voile. */
  .neko-ps{padding:0; align-items:flex-end}
  .neko-ps-box{max-height:88vh; border-radius:24px 24px 0 0}
}


.jd-hero-tag.is-sv{left:auto; right:14px; background:rgba(31,26,26,.62); color:#FBF7F3}
.jd-body{padding:20px 22px 22px}
.jd-head{display:flex; align-items:baseline; justify-content:space-between; gap:14px}
.jd-title{font-family:'Lora',Georgia,serif; font-size:23px; font-weight:600;
  letter-spacing:-.01em; color:var(--ink); margin:0}
.jd-num{font-family:var(--mono); font-size:10px; letter-spacing:.06em; color:var(--muted); white-space:nowrap}
.jd-author{display:block; font-family:var(--mono); font-size:9.5px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--terracotta); margin-top:14px}
.jd-quote{font-family:'Lora',Georgia,serif; font-size:15.5px; line-height:1.6;
  color:var(--ink-soft); margin:14px 0 0}
.jd-author + .jd-quote{margin-top:5px}
.jd-quote + .jd-quote{margin-top:9px}
/* ---------- LE MOT DU SITTER, EN CARTE ET EN TÊTE DE JOURNÉE (2026-08-30) ----
   ⚠️ Il vivait SOUS les blocs de preuve, en citation nue posée sur le fond. Il
   passe au-dessus, dans une carte à lui : c'est la seule chose de la journée
   qu'une personne a écrite pour une autre, et la preuve n'est là que pour
   l'attester. L'en-tête nomme l'auteur et donne l'heure (« MOT DE CAMILLE ·
   19:14 »). Miroir de `JournalDayCard.tsx` — les deux écrans montrent la même
   journée, ils bougent ensemble. */
.jd-note{background:var(--white); border:1px solid var(--line); border-radius:14px;
  padding:14px 15px; margin-bottom:10px}
.jd-note + .jd-note{margin-top:10px}
.jd-note-h{display:flex; align-items:baseline; justify-content:space-between; gap:12px;
  font-family:var(--mono); font-size:9.5px; letter-spacing:.14em; color:var(--muted)}
.jd-note .jd-quote{margin:8px 0 0}
.jd-note .jd-quote + .jd-quote{margin-top:9px}
/* Vignettes : la photo du jour est déjà en grand au-dessus, celles-ci sont le
   reste de la journée. Au-delà de trois, la dernière tuile devient « +N » et
   déplie le reste sur place (rien n'est perdu, rien ne pousse la carte). */
.jd-thumbs{display:flex; flex-wrap:wrap; gap:8px; margin-top:16px}
.jd-thumbs img, .jd-more{width:60px; height:60px; border-radius:12px; flex:0 0 auto}
.jd-thumbs img{object-fit:cover; cursor:zoom-in; background:var(--cream-2)}
.jd-more{display:grid; place-items:center; background:var(--cream-2); color:var(--muted);
  font-family:var(--mono); font-size:12px; cursor:pointer; border:0; padding:0}
/* Vidéo souvenir de la journée (2026-08-09). Une seule règle pour les DEUX mises
   en page : elles sont montées ensemble, seule leur visibilité diffère. */
.jd-video{margin-top:16px; border-radius:14px; overflow:hidden; background:#000; line-height:0}
.jd-video video{width:100%; max-height:320px; display:block; object-fit:contain; background:#000}
.jd-more:hover{color:var(--ink)}
.jd-rule{height:1px; background:var(--line); margin:20px 0 13px}
.jd-care-h{font-family:var(--mono); font-size:9.5px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--muted)}
/* ⚠️ 13.5px → 12px (2026-08-30, demande Julie) : le relevé « Pâtée ✓ Croquettes ✓
   Eau ✓ » est une VÉRIFICATION, pas une phrase à lire — à taille de texte courant
   il pesait autant que le mot du sitter juste au-dessus, qui est le seul contenu
   écrit par une personne pour une autre. */
.jd-care{display:flex; flex-wrap:wrap; gap:5px 16px; margin-top:10px; font-size:12px; color:var(--ink)}
/* ✓ = pointé par le sitter, ○ = pas pointé ce jour-là. Jamais un tiret cadratin
   (règle de copy du projet), et jamais une croix : ce n'est pas un reproche. */
.jd-care i{font-style:normal; color:var(--terracotta); margin-left:5px}
.jd-care .off{color:var(--muted)}
.jd-care .off i{color:var(--muted)}

/* ============================================================================
   FEUILLETAGE = VRAI RAIL QUI DÉFILE (2026-07-31, partagé depuis le 2026-08-10)
   ⚠️ Les journées ne sont pas rendues UNE À LA FOIS : elles sont toutes dans le
   DOM, côte à côte, dans un conteneur qui défile horizontalement avec accrochage
   (`scroll-snap`). Avant, changer de jour exigeait de viser une pastille de 6px
   ou une petite date en bout de ligne, et le glissement du doigt était simulé en
   JS (touchstart/touchend) : il ne marchait donc ni à la souris, ni au pavé
   tactile, ni au clavier, c'est-à-dire nulle part ailleurs que sur un téléphone.
   Le défilement natif marche partout et donne le suivi du doigt.
   ⚠️ MONTÉ PAR DEUX PAGES : /mon-espace et /journal-garde. La mécanique JS vit
   dans `nekoDayRail` (shared.js) et le sens de lecture (rail retourné) y est
   décrit une seule fois — ne PAS redéfinir ces classes dans une page.
   ⚠️ `scroll-snap-stop:always` : un jour à la fois. Sans lui, une chiquenaude un
   peu vive saute trois journées, et un journal se lit jour après jour.
   ⚠️ Le halo latéral de la carte est COUPÉ par le rail (tout conteneur qui
   défile rogne à ses bords), la carte faisant exactement sa largeur. Compromis
   accepté : le halo qui compte est celui du HAUT, préservé par le `padding`.
   ⚠️ `align-items:flex-start` : sans lui, toutes les cartes prennent la hauteur
   de la plus haute, et une journée sans photo devient un grand rectangle blanc.
   ============================================================================ */
.jd-track{display:flex; align-items:flex-start; gap:14px; overflow-x:auto;
  scroll-snap-type:x mandatory; overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch; padding:7px 0 10px; scrollbar-width:none}
.jd-track::-webkit-scrollbar{display:none}
.jd-track>.jd-card{flex:0 0 100%; scroll-snap-align:center; scroll-snap-stop:always}
/* Pendant un glissement à la souris : pas de sélection de texte en travers des
   cartes, et le curseur dit que le rail suit la main. */
.jd-track.is-dragging{cursor:grabbing; user-select:none}
.jd-track.is-dragging img{pointer-events:none}
/* Feuilletage : jour précédent à gauche, pastilles au centre, suivant à droite.
   Grille 1fr auto 1fr → les pastilles restent centrées même quand un seul des
   deux côtés porte une date. */
.jd-nav{display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:12px;
  max-width:520px; margin:16px auto 0}
.jd-step{font-family:var(--mono); font-size:11px; letter-spacing:.04em; color:var(--muted);
  background:none; border:0; padding:8px 0; cursor:pointer; transition:color .15s}
.jd-step:hover{color:var(--terracotta)}
.jd-step.is-next{text-align:right}
.jd-dots{display:flex; align-items:center; gap:7px}
.jd-dot{width:6px; height:6px; border-radius:50%; background:rgba(31,26,23,.18);
  border:0; padding:0; cursor:pointer; transition:background .15s}
.jd-dot:hover{background:rgba(31,26,23,.34)}
.jd-dot.on{background:var(--terracotta)}

/* ============ Recadrage carré d'une photo (nekoCropSquare, shared.js) ============
   Le viseur est un CARRÉ — c'est ce qui est exporté — et le cercle par-dessus
   n'est qu'un guide : il montre la part que le médaillon du tableau de bord et
   la fiche publique afficheront. Voir le pavé de `nekoCropSquare` dans
   `shared.js` pour le pourquoi ; ne pas dupliquer l'explication ici.

   ⚠️ `z-index:4100` : au-dessus de la lightbox (4000). Le recadreur s'ouvre
   depuis un écran d'édition, jamais l'inverse, donc rien ne doit passer devant.
   ⚠️ `touch-action:none` sur le viseur n'est PAS décoratif : sans lui le doigt
   fait défiler la page et la photo ne bouge pas d'un pixel sur téléphone. */
.neko-crop{position:fixed; inset:0; z-index:4100; display:flex; align-items:center; justify-content:center; padding:18px;
  background:rgba(251,247,243,.94); overscroll-behavior:contain}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .neko-crop{background:rgba(251,247,243,.5); -webkit-backdrop-filter:blur(30px) saturate(1.2); backdrop-filter:blur(30px) saturate(1.2)}
}
.neko-crop-box{background:var(--white); border:1px solid var(--line); border-radius:var(--radius-lg);
  box-shadow:var(--shadow-lift); padding:20px 20px 18px; max-width:min(92vw, 400px); text-align:center}
.neko-crop-h{margin:0; font-family:var(--display); font-weight:500; font-size:19px; color:var(--ink); letter-spacing:-.01em}
.neko-crop-hint{margin:6px 0 14px; font-family:var(--sans); font-size:12.5px; line-height:1.5; color:var(--muted)}

.neko-crop-stage{position:relative; margin:0 auto; border-radius:10px; overflow:hidden; background:var(--cream-2);
  cursor:grab; touch-action:none; user-select:none; -webkit-user-select:none}
.neko-crop-stage:active{cursor:grabbing}
.neko-crop-canvas{display:block}
/* Le cercle est dessiné par une ombre INTÉRIEURE démesurée : elle assombrit
   tout ce qui l'entoure jusqu'aux bords du carré, sans masque ni SVG. Les coins
   restent visibles — ils sont conservés dans le fichier, les cacher mentirait. */
.neko-crop-mask{position:absolute; inset:0; border-radius:50%; pointer-events:none;
  box-shadow:0 0 0 9999px rgba(31,26,26,.34); border:1.5px solid rgba(255,255,255,.9)}

.neko-crop-zoom{display:flex; align-items:center; gap:10px; margin:14px 2px 4px}
.neko-crop-zi{width:9px; height:9px; flex:0 0 auto; border-radius:2px; background:var(--muted)}
.neko-crop-zi.is-big{width:15px; height:15px; border-radius:3px}
.neko-crop-zoom input[type=range]{flex:1; min-width:0; accent-color:var(--terracotta); cursor:pointer}

.neko-crop-actions{display:flex; gap:10px; margin-top:14px}
.neko-crop-actions button{flex:1; min-height:44px; border-radius:999px; font-family:var(--sans); font-size:14px; cursor:pointer;
  transition:background .15s, border-color .15s}
.neko-crop-cancel{border:1px solid var(--line); background:var(--white); color:var(--ink-soft)}
.neko-crop-cancel:hover{border-color:var(--muted)}
.neko-crop-ok{border:none; background:var(--terracotta); color:#fff; font-weight:600}
.neko-crop-ok:hover{background:var(--terracotta-deep)}
.neko-crop-ok:disabled{opacity:.6; cursor:default}


/* ══════════════════════════════════════════════════════════════════════════
   « MON COMPTE » — L'ASSEMBLAGE DESKTOP (2026-08-30, maquette Julie)

   Portée : /profil-proprietaire et /profil-sitter, les DEUX SEULES pages qui
   portent les classes `.ap-*` (vérifié : aucun autre fichier du site ne les
   emploie). Le reste de leur habillage vit encore dans le <style> de chaque
   page, en deux copies ; ce bloc-ci est écrit UNE fois exprès, parce que c'est
   l'assemblage — pas les composants — qui a divergé le plus vite entre elles.

   Ce que la maquette change, et rien d'autre :
   · un EN-TÊTE de page (titre + une ligne d'état) au-dessus des deux colonnes ;
   · la colonne de gauche devient un MENU étroit (carte d'identité, groupes,
     lignes compactes, filet vertical) au lieu d'une pile de cartes blanches ;
   · la colonne de droite n'est plus UNE carte : c'est la surface de la page, et
     ce sont les blocs à l'intérieur qui sont des cartes ;
   · le bouton d'enregistrement remonte en tête du panneau, face au titre.

   ⚠️ RIEN SOUS 1024px. Le format téléphone garde l'écran « app-like » (pile de
   cartes + tiroir coulissant) : tout est dans le @media, et les deux règles
   hors média ci-dessous ne font que NEUTRALISER les nœuds ajoutés pour le
   desktop, afin que le mobile ne les voie pas.
   ⚠️ `body:not(.embed)` partout : en `?embed=compte`, /profil-sitter vit dans
   l'iframe de /moyens-de-paiement, qui porte déjà sa colonne et sa carte — cf.
   le pavé `body.embed` de cette page-là, qui aplatit tout ce qui suit.
   ══════════════════════════════════════════════════════════════════════════ */

/* Nœuds qui n'existent QUE pour l'assemblage desktop. `display:contents` et non
   `display:none` pour les deux conteneurs de champs : en mobile les champs se
   suivent à même le panneau, la carte qui les regroupe ne doit pas exister. */
.ap-head-sub, .ap-hero-mail, .ap-photo, .ap-subcards, .ap-back{display:none}
/* Carte d'identité du profil SITTER : elle n'existe qu'en desktop. La page
   n'en avait aucune, et pour une raison qui tient toujours — photo, nom
   d'affichage et ville se règlent sur le profil public, pas dans les
   réglages. Celle-ci n'édite rien, elle nomme le compte en tête du menu. */
/* ⚠️ `.ap-hero.ap-hero-me` et pas `.ap-hero-me` seul : la feuille de la page
   est chargée APRÈS celle-ci et y déclare `.ap-hero{display:flex}`, de même
   spécificité — à égalité c'est l'ordre qui tranche, et la carte serait
   apparue en mobile. */
.ap-hero.ap-hero-me{display:none}
.ap-fieldset, .ap-pair{display:contents}

@media (min-width:1024px){
  body:not(.embed) .ap-wrap{max-width:1320px}

  /* ── En-tête de page ──
     La ligne d'état (prénom · ancienneté · nombre de chats) reprend ce que le
     grand médaillon de l'app affichait : il devient une carte de menu, ces
     informations-là ne sont pas supprimées, elles remontent ici. */
  body:not(.embed) .ap-head{margin:0 0 30px; padding-bottom:22px; border-bottom:1px solid var(--line)}
  body:not(.embed) .ap-head .ap-title{margin:0; font-size:38px}
  body:not(.embed) .ap-head-sub{display:block; margin:8px 0 0; font-size:15.5px; color:var(--muted)}
  /* Retour au tableau de bord. ⚠️ IL EST LA SEULE SORTIE de cette page en
     desktop depuis que le menu vertical n'y est plus (bloc `body.ap-solo`, plus
     bas) : sans lui, un écran de réglages sans issue. Masqué sous 1025px, où le
     dock du bas porte la navigation. */
  body:not(.embed) .ap-back{display:inline-flex; align-items:center; gap:7px; margin:0 0 12px;
    font-family:var(--mono); font-size:12.5px; font-weight:700; letter-spacing:.12em;
    text-transform:uppercase; color:var(--muted); text-decoration:none}
  body:not(.embed) .ap-back:hover{color:var(--terracotta)}

  /* ── Les deux colonnes ──
     Séparées par un filet : le menu ne se dessine plus en cartes blanches, c'est
     la colonne elle-même qui est délimitée. */
  body:not(.embed) .ap-shell{display:grid; grid-template-columns:minmax(248px,296px) minmax(0,1fr); gap:0; align-items:start}
  body:not(.embed) .ap-main{min-width:0; padding-right:30px; border-right:1px solid var(--line)}
  body:not(.embed) .ap-detail{display:block; position:static; padding-left:40px}
  body:not(.embed) .ap-detail-backdrop, body:not(.embed) .ap-detail-close{display:none}
  body:not(.embed) .ap-detail-inner{background:none; border-radius:0; box-shadow:none;
    padding:0; width:auto; max-width:none; height:auto; max-height:none; overflow:visible}

  /* ── Carte d'identité en tête du menu ──
     C'est le HÉROS de l'app, resserré : même nœud, mêmes données, même clic. */
  body:not(.embed) .ap-hero.ap-hero-me{display:flex}
  body:not(.embed) .ap-main > .ap-hero{gap:12px; padding:12px; margin:0 0 6px; border-radius:14px}
  body:not(.embed) .ap-main > .ap-hero .avatar.xl{width:46px; height:46px; border-radius:12px; font-size:18px}
  body:not(.embed) .ap-main > .ap-hero .ap-hero-name{font-family:var(--sans); font-size:17px; font-weight:700}
  body:not(.embed) .ap-main > .ap-hero .ap-hero-name svg{width:15px; height:15px}
  body:not(.embed) .ap-main > .ap-hero .ap-hero-sub,
  body:not(.embed) .ap-main > .ap-hero .ap-hero-stats,
  body:not(.embed) .ap-main > .ap-hero .ap-hero-chev{display:none}
  body:not(.embed) .ap-hero-mail{display:block; margin-top:3px; font-size:14px; color:var(--muted);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  /* La carte d'identité est un déclencheur de panneau comme les autres (elle
     ouvre l'aperçu du profil public) : elle porte donc la même marque d'ouvert
     que les lignes du menu. */
  body:not(.embed) .ap-main > .ap-hero.active{background:var(--cream-3)}
  body:not(.embed) .ap-main > .ap-hero.active .ap-hero-name{color:var(--terracotta)}

  /* ── Groupes et lignes du menu ── */
  body:not(.embed) .ap-grouplabel{margin:26px 0 7px; font-family:var(--mono); font-size:12.5px;
    font-weight:700; letter-spacing:.12em; color:var(--muted)}
  body:not(.embed) .ap-list{background:none; border-radius:0; overflow:visible}
  body:not(.embed) .ap-list .ap-row,
  body:not(.embed) .ap-list button.ap-row{padding:12px 13px; gap:12px; font-size:16.5px;
    border-bottom:0; border-radius:12px}
  body:not(.embed) .ap-rowic{width:23px}
  body:not(.embed) .ap-rowic svg{width:19px; height:19px}
  body:not(.embed) .ap-chev{font-size:18px}
  /* ⚠️ La valeur grise de droite DISPARAÎT : 200px de colonne ne la tiennent
     pas, et elle répète pour l'essentiel ce que la ligne dit déjà. Restent les
     deux ÉTATS — la pastille verte « Vérifiée » et la mention « À faire » —
     parce qu'eux seuls disent où il reste quelque chose à faire. Ne pas
     rallumer `.ap-rowval` en bloc : c'est la mention à faire qu'on perdrait de
     vue au milieu des « Français », « Push, e-mail » et « Visa •••• 4242 ». */
  body:not(.embed) .ap-rowval{display:none}
  body:not(.embed) .ap-rowval.is-badge, body:not(.embed) .ap-rowval.is-todo{display:inline-block}
  body:not(.embed) .ap-rowval.is-badge{font-size:11.5px; padding:4px 9px}
  body:not(.embed) .ap-rowval.is-todo{max-width:none; overflow:visible; font-family:var(--mono);
    font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--terracotta)}
  /* Ligne ouverte : aplat ET libellé corail. L'aplat seul se confondrait avec le
     survol, qui porte déjà un crème. */
  body:not(.embed) .ap-list .ap-row.active,
  body:not(.embed) .ap-list button.ap-row.active{background:var(--cream-2)}
  body:not(.embed) .ap-list .ap-row.active .ap-rowlb{color:var(--terracotta); font-weight:600}
  body:not(.embed) .ap-list .ap-row.active .ap-rowic{color:var(--terracotta)}

  /* ── Pied du menu ──
     ⚠️ LE SIGNALEMENT REDEVIENT UN VRAI CTA (2026-08-31, demande Julie : « je
     veux un style de CTA cohérent avec le reste de mon site, mets-le à gauche
     il y a de la place »). Il a été un bouton blanc discret du 2026-08-30 à ce
     jour, au motif qu'un aplat corail pèserait dans une colonne de navigation ;
     à l'usage il ne se lisait plus comme une action. Ne pas restaurer la
     version blanche en relisant ce commentaire-là.
     ⚠️ Le dessin est celui de `.btn-primary` (§ « CTA — bouton 3D corail »), et
     il est RECOPIÉ ici plutôt qu'emprunté : le bouton porte `.ap-cta`, pas
     `.btn`, et lui ajouter la classe le ferait hériter d'un `min-height:48px`
     et d'un `padding:14px 26px` taillés pour une pleine page. Les trois valeurs
     qui font la signature — rayon 30px, liseré encre décalé de 3px, tranche
     #C44C3D — bougent donc AVEC ce paragraphe-là.
     ⚠️ CE BOUTON N'EST PLUS DANS LA COLONNE (2026-08-31) : il est remonté en
     haut à droite de la page, avec la déconnexion — cf. le bloc « LES DEUX
     ACTIONS DU MENU », en fin de ce média, qui porte sa position, sa marge et
     son alignement. Les valeurs ci-dessous ne décrivent QUE son dessin.
     ⚠️ Ne pas restaurer le `justify-content:flex-start` ni la marge haute en
     relisant ce paragraphe : ils appartenaient à l'époque où il fermait la
     colonne de gauche. */
  body:not(.embed) .ap-main > .ap-cta.ap-cta-flat{position:relative; justify-content:flex-start;
    margin:28px 0 0; padding:14px 22px; gap:10px;
    background:var(--terracotta); color:#fff; font-size:16px;
    border:1px solid transparent; border-radius:30px; box-shadow:0 1px 0 0 #C44C3D;
    transition:transform .1s ease, box-shadow .1s ease, background .2s}
  /* Liseré encre décalé vers le haut — le trait « dessiné » des CTA du site. */
  body:not(.embed) .ap-main > .ap-cta.ap-cta-flat::before{content:""; position:absolute; inset:0;
    border:1px solid #1F1A1A; border-radius:inherit; transform:translateY(-3px); pointer-events:none}
  body:not(.embed) .ap-main > .ap-cta.ap-cta-flat:hover{background:var(--terracotta);
    transform:translateY(-1px); box-shadow:0 2px 0 0 #C44C3D}
  body:not(.embed) .ap-main > .ap-cta.ap-cta-flat:active{transform:translateY(1px);
    box-shadow:0 0 0 0 #C44C3D}
  body:not(.embed) .ap-main > .ap-cta.ap-cta-flat svg{color:#fff; width:18px; height:18px}
  @media (prefers-reduced-motion: reduce){
    body:not(.embed) .ap-main > .ap-cta.ap-cta-flat{transition:background .2s}
    body:not(.embed) .ap-main > .ap-cta.ap-cta-flat:hover{transform:none}
  }
  /* ⚠️ Le pied de menu (« Se déconnecter ») est lui aussi DÉPLACÉ en haut à
     droite : sa position, sa marge, son alignement et sa taille se règlent
     dans le bloc « LES DEUX ACTIONS DU MENU », en fin de ce média. Ne restent
     ici que sa couleur et ce que la ligne n'affiche pas. */
  body:not(.embed) .ap-list-foot .ap-row{color:var(--muted)}
  body:not(.embed) .ap-list-foot .ap-row:hover{background:none; color:var(--terracotta)}
  body:not(.embed) .ap-list-foot .ap-rowic, body:not(.embed) .ap-list-foot .ap-chev{display:none}
  /* Le logo reste : pour un propriétaire connecté, cette page ne rend AUCUNE
     navigation (cf. le commentaire de son script) — c'est le seul retour vers
     l'accueil. Il rétrécit, il ne part pas. */
  body:not(.embed) .ap-home{margin:20px 0 0}
  body:not(.embed) .ap-home img{width:46px; height:46px}
  body:not(.embed) .ap-version{display:none}

  /* ── Le panneau de droite ──
     Trois places fixes en tête : le chapeau (le groupe d'où vient la ligne
     ouverte, porté par `data-eyebrow`), le titre, et le bouton d'action en
     vis-à-vis à droite.
     ⚠️ Le bouton n'est PAS déplacé dans le HTML : il reste en fin de formulaire,
     qui est sa place en mobile, où le panneau est un tiroir. C'est la grille qui
     le remonte, et elle seule — d'où les `grid-row` explicites sur ces trois-là,
     tout le reste s'auto-plaçant en dessous parce qu'il occupe les deux
     colonnes et ne peut donc pas tenir dans les deux premières rangées. */
  /* ⚠️ `:not([hidden])` N'EST PAS DÉCORATIF. `apSelect` masque les panneaux par
     l'ATTRIBUT `hidden`, et la règle qui l'applique (`.ap-panel[hidden]`, dans la
     feuille de chaque page) pèse (0,2,0) — moins que `body:not(.embed) .ap-panel`
     (0,2,1). Sans ce garde, TOUS les panneaux s'afficheraient d'un coup, empilés.
     Même piège et même correctif que `body.embed .ap-detail-empty:not([hidden])`
     de /profil-sitter. */
  body:not(.embed) .ap-panel:not([hidden]){display:grid; grid-template-columns:minmax(0,1fr) auto;
    align-items:start; column-gap:18px}
  body:not(.embed) .ap-panel > *{grid-column:1 / -1}
  body:not(.embed) .ap-panel[data-eyebrow]::before{content:attr(data-eyebrow);
    grid-column:1; grid-row:1; font-family:var(--mono); font-size:11px; font-weight:700;
    letter-spacing:.14em; text-transform:uppercase; color:var(--muted)}
  body:not(.embed) .ap-panel > .ap-modal-title{grid-column:1; grid-row:2; margin:4px 0 0; font-size:28px}
  body:not(.embed) .ap-panel > .ap-panel-action{grid-column:2; grid-row:1 / span 2;
    align-self:center; justify-self:end; margin:0; min-height:48px; padding:13px 26px; font-size:15.5px}
  body:not(.embed) .ap-panel > .ap-panel-note{margin:16px 0 0; max-width:66ch}
  /* Une liste DANS un panneau (les six blocs de « Mes protections ») reste une
     carte blanche : la règle du menu, juste au-dessus, ne vaut que pour la
     colonne de gauche. */
  body:not(.embed) .ap-panel .ap-list{margin-top:22px; background:var(--white);
    border:1px solid var(--line); border-radius:16px; overflow:hidden}

  /* ── Les blocs de contenu sont des CARTES, le panneau n'en est plus une ── */
  body:not(.embed) .ap-fieldset{display:block; margin-top:22px; padding:24px 24px 6px;
    background:var(--white); border:1px solid var(--line); border-radius:16px}
  /* ⚠️ Le liseré des champs REVIENT ici. Il avait été retiré parce que le blanc
     de l'input suffisait à le dessiner sur le crème du panneau ; sur une carte
     blanche, un champ blanc sans liseré n'est plus un champ, c'est un blanc. */
  body:not(.embed) .ap-fieldset .field input,
  body:not(.embed) .ap-fieldset .field textarea,
  body:not(.embed) .ap-fieldset .field select{border-color:var(--line)}
  body:not(.embed) .ap-fieldset .field input:focus,
  body:not(.embed) .ap-fieldset .field textarea:focus,
  body:not(.embed) .ap-fieldset .field select:focus{border-color:var(--terracotta)}
  body:not(.embed) .ap-pair{display:grid; grid-template-columns:1fr 1fr; gap:18px; margin-bottom:18px}
  body:not(.embed) .ap-pair > .field{margin-bottom:0}

  /* Ligne « photo de profil », en tête de la carte. */
  body:not(.embed) .ap-photo{display:flex; align-items:center; gap:14px;
    padding-bottom:22px; margin-bottom:22px; border-bottom:1px solid var(--line)}
  body:not(.embed) .ap-photo .avatar{width:60px; height:60px; border-radius:14px; font-size:23px}
  body:not(.embed) .ap-photo-tx{flex:1; min-width:0}
  body:not(.embed) .ap-photo-tx b{display:block; font-size:15.5px; color:var(--ink)}
  body:not(.embed) .ap-photo-tx small{display:block; margin-top:4px; font-size:13px; line-height:1.5; color:var(--muted)}

  /* ── Deux cartes secondaires sous la carte principale ── */
  body:not(.embed) .ap-subcards{display:grid; grid-template-columns:1fr 1fr; gap:18px; margin-top:18px}
  body:not(.embed) .ap-subcard{padding:20px; background:var(--white); border:1px solid var(--line); border-radius:16px}
  body:not(.embed) .ap-subcard.is-wide{grid-column:1 / -1}
  body:not(.embed) .ap-subcard-h{margin:0 0 12px; font-family:var(--mono); font-size:10.5px; font-weight:700;
    letter-spacing:.14em; text-transform:uppercase; color:var(--muted)}
  /* La carte d'invite porte le corail : c'est la seule des deux sur laquelle il
     reste quelque chose à faire. */
  body:not(.embed) .ap-subcard-todo{background:color-mix(in oklab, var(--terracotta) 7%, var(--white));
    border-color:color-mix(in oklab, var(--terracotta) 28%, var(--white))}
  body:not(.embed) .ap-subcard-todo .ap-subcard-h{color:var(--terracotta)}
  body:not(.embed) .ap-subcard-todo b{display:block; font-size:15.5px; color:var(--ink)}
  body:not(.embed) .ap-subcard-todo p{margin:6px 0 14px; font-size:13.5px; line-height:1.55; color:var(--ink-soft)}
  body:not(.embed) .ap-subcard .btn{min-height:40px; padding:10px 18px; font-size:14px}
  body:not(.embed) .ap-catrow{display:flex; align-items:center; gap:12px; padding:6px 0}
  body:not(.embed) .ap-catrow .avatar{width:38px; height:38px; border-radius:10px; font-size:15px}
  body:not(.embed) .ap-catrow-tx{flex:1; min-width:0}
  body:not(.embed) .ap-catrow-tx b{display:block; font-size:14.5px; color:var(--ink)}
  body:not(.embed) .ap-catrow-tx small{display:block; margin-top:2px; font-size:12.5px; color:var(--muted)}
  body:not(.embed) .ap-catrow a{flex:0 0 auto; font-size:13.5px; font-weight:600; color:var(--terracotta)}
  body:not(.embed) .ap-catadd{display:inline-flex; align-items:center; gap:7px; margin-top:10px;
    font-size:13.5px; color:var(--muted)}
  body:not(.embed) .ap-catadd:hover{color:var(--terracotta)}
  body:not(.embed) .ap-subcard-empty{margin:0; font-size:13.5px; line-height:1.55; color:var(--muted)}

  /* Panneaux embarqués (iframe) : ils occupent la surface, sans carte autour —
     la page qu'ils servent porte déjà la sienne. */

  /* ── Echelle de texte du panneau (2026-08-31, demande Julie : « agrandis
     tout »). Le panneau de droite a desormais la largeur d'une page, pas celle
     du tiroir de 460px pour lequel ces tailles avaient ete posees : a cette
     largeur, 13px de chapo et 10px d'etiquette se lisent comme des notes de bas
     de page.
     ⚠️ Tout ce qui suit ne vaut QU'au-dessus de 1024px — le tiroir mobile garde
     ses tailles, elles y sont justes. Ne pas les remonter dans la feuille de
     page : elles y vaudraient aussi pour le tiroir. */
  body:not(.embed) .ap-panel .ap-panel-note{font-size:15px; line-height:1.6}
  body:not(.embed) .ap-panel .field{margin-bottom:18px; gap:9px}
  body:not(.embed) .ap-panel .field label{font-size:11px}
  body:not(.embed) .ap-panel .field input,
  body:not(.embed) .ap-panel .field textarea,
  body:not(.embed) .ap-panel .field select{padding:14px 16px; font-size:16px; border-radius:12px}
  body:not(.embed) .ap-panel .field .help{font-size:13px}
  body:not(.embed) .ap-panel .ap-form-head h4{font-size:18px}
  body:not(.embed) .ap-panel .ap-form-head p{font-size:14px}
  body:not(.embed) .ap-panel .ap-prot{gap:14px; padding:16px 18px}
  body:not(.embed) .ap-panel .ap-prot > svg{width:18px; height:18px}
  body:not(.embed) .ap-panel .ap-prot b{font-size:14.5px}
  body:not(.embed) .ap-panel .ap-prot small{font-size:13px}
  body:not(.embed) .ap-panel .ap-toggle{padding:16px 18px; border-radius:16px; margin-bottom:14px}
  body:not(.embed) .ap-panel .ap-toggle b{font-size:15.5px}
  body:not(.embed) .ap-panel .ap-toggle small{font-size:13px}
  body:not(.embed) .ap-panel .ap-toggle input{width:22px; height:22px}
  body:not(.embed) .ap-panel .ap-idv{padding:18px; border-radius:18px}
  body:not(.embed) .ap-panel .ap-idv-tx b{font-size:15.5px}
  body:not(.embed) .ap-panel .ap-idv-tx small{font-size:13px}
  body:not(.embed) .ap-panel .save-feedback{font-size:14px}

  /* ── LES DEUX ACTIONS DU MENU REMONTENT EN HAUT A DROITE (2026-08-31,
     demande Julie : « deplace ca dans la partie droite de la page » pour
     « Signaler un probleme », « en haut a droite » pour la deconnexion).
     Elles fermaient la colonne de gauche, sous les quatre groupes de reglages ;
     ce sont les deux seules actions de l'ecran qui ne reglent rien, et le cote
     droit de l'en-tete etait vide.
     ⚠️ DEPLACEES EN CSS, PAS EN HTML, et c'est delibere : sous 1024px les deux
     noeuds doivent rester en FIN de pile, a leur place « app-like » (miroir de
     mobile/app/profile.tsx). Les remonter dans `.ap-head` cote HTML les ferait
     apparaitre juste sous le titre, avant le medaillon, sur telephone.
     ⚠️ L'ancre est `#form-wrap` et pas `.ap-head` : le padding et la bordure
     basse de l'en-tete bougent avec son contenu, le bord haut de la page non.
     ⚠️ `padding-right` sur `.ap-head` n'est pas decoratif : sans lui la ligne
     d'etat (`paintHeadSub`, longueur variable) passerait SOUS le bouton corail.
     ⚠️ Les deux gardent leur `data-popup` / leur handler : c'est le meme noeud
     deplace, pas une copie. Ne pas en ajouter une seconde dans l'en-tete. */
  body:not(.embed) #form-wrap{position:relative}
  body:not(.embed) .ap-head{padding-right:260px}
  body:not(.embed) .ap-list-foot{position:absolute; top:0; right:0; z-index:2; margin:0; width:auto}
  body:not(.embed) .ap-list-foot .ap-row{justify-content:flex-end; padding:0; font-size:14px}
  body:not(.embed) .ap-main > .ap-cta.ap-cta-flat{position:absolute; top:28px; right:0; z-index:2;
    justify-content:center; width:auto; margin:0}
  body:not(.embed) .ap-embed{min-height:calc(100vh - 260px)}
}

/* ══ « MON COMPTE » EST UNE PAGE SEULE (2026-08-30, demande Julie : « enlève le
   menu à gauche et la messagerie à droite »). Ni menu vertical, ni colonne
   messagerie : l'écran de réglages porte déjà sa propre colonne de navigation,
   et deux menus côte à côte se disputaient la lecture.

   ⚠️ MASQUER LES DEUX COLONNES NE SUFFIT PAS. Elles sont en `position:fixed`,
   donc shared.css leur RÉSERVE leur largeur sur le body, et cette réservation
   survit à leur `display:none` : le contenu resterait poussé de 296px à gauche
   (`body.has-tabbar`) et de 356px à droite (`body.has-owner-rail`), soit une
   page décalée qui ne touche plus aucun bord. Même piège, même correctif et même
   date de découverte que la maquette « sans sidebar » de /mon-espace.
   ⚠️ Le trou ne se voit PAS déconnecté : sans session ni menu ni rail ne se
   montent, donc aucune des deux classes n'est posée. Ne pas conclure d'une
   capture « session expirée » que la mise en page est bonne.
   ⚠️ `body.neko-msg-pinned{padding-right}` ne sert à rien ici : les deux pages
   passent `{ pin:false }` à `neckoMountSideRail`, cette classe n'est donc jamais
   posée. C'est `has-owner-rail` qu'il faut viser.
   ⚠️ `{ pin:false }` N'EST PAS FACULTATIF : sans lui shared.js déplace le tiroir
   de messagerie DANS la colonne masquée, et `nekoOpenMessagerie()` sort en tête
   sans rien ouvrir — la messagerie devient injoignable en silence.
   ⚠️ SEUIL 1025px, pas 1024 : c'est celui du chrome connecté
   (`neko-mobile-shell`). Sous lui il n'y a ni menu vertical ni colonne, le dock
   du bas porte la navigation, et il ne faut surtout pas le masquer. ══ */
@media (min-width:1025px){
  body.ap-solo #neko-tabbar,
  body.ap-solo .neko-owner-rail.is-msg{display:none}
  body.ap-solo.has-tabbar{padding-left:0}
  body.ap-solo.has-owner-rail{padding-right:0}
}
