/*! Longward — personal wealth dashboard
 *  Copyright (C) 2026 Longward
 *  Licensed under the GNU Affero General Public License, version 3 or later.
 *  Source: https://github.com/pierrelouis-fetet/Longward-demo
 *  Distributed WITHOUT ANY WARRANTY. See the LICENSE file for the full terms.
 */
.goal { display: flex; flex-direction: column; gap: 10px; }

.runway { list-style: none; margin: 14px 0 0; padding: 0; }
.rw-ligne { padding: 10px 0; border-bottom: 1px solid var(--border); }
.rw-ligne:last-child { border-bottom: none; padding-bottom: 2px; }
.rw-haut, .rw-bas { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.rw-bas { margin-top: 3px; }
.rw-lab { font-weight: 550; min-width: 0; }
.rw-val { flex: none; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rw-note { min-width: 0; font-size: var(--font-sm); line-height: 1.45; color: var(--muted); }
.rw-mois { flex: none; }

/* Le montant et sa légende côte à côte, pas aux deux bouts de la carte.

   `space-between` marchait sur un téléphone, où 343 px font une paire serrée.
   Sur un écran de 1 280 px il jetait le chiffre tout à gauche et la phrase tout
   à droite, avec neuf cents pixels de vide entre les deux : deux îlots au lieu
   d'une lecture. « Ça manque de professionnalisme » — c'est d'abord ça.

   Alignés à gauche, ils se lisent d'un trait à toutes les largeurs. La barre,
   elle, garde la pleine largeur : c'est une jauge, elle doit se comparer à son
   propre vide. */
.goal-top { display: flex; justify-content: flex-start; gap: 12px; align-items: baseline; }
.goal-top b { font-size: var(--font-xl); letter-spacing: -.02em; }
.goal-top-empile { flex-wrap: wrap; gap: 2px 12px; }
.goal-top-empile .muted { min-width: 0; font-size: var(--font-sm); }
.goal-bar { height: 12px; border-radius: var(--radius-pill); background: var(--surface-2); overflow: hidden; border: 1px solid var(--border); }
/* L'accent, et non le dégradé du budget.

   La barre portait `--degrade-budget`, bleu vers rose, créé pour accorder la
   jauge de l'accueil et les barres de catégories du Budget. Sur Projection,
   aucun autre élément ne le porte : le graphique juste en dessous parle en azur
   et en turquoise. La barre était donc la seule couleur décorative de l'écran,
   et un dégradé qui ne représente aucune quantité se lit comme de l'ornement.
   « Ça ne colle pas au graphique plus bas » : les deux ne partageaient
   littéralement aucune couleur. */
.goal-fill { height: 100%; border-radius: var(--radius-pill); background: var(--accent); }
.goal-foot { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 4px 12px;
             font-size: var(--font-sm); color: var(--text-secondary); }
.goal-depart { display: block; font-size: var(--font-xs); }
.goal-vide {
  flex-direction: row; align-items: center; justify-content: space-between;
  gap: 12px; font-size: var(--font-md); color: var(--text-secondary);
}
.goal-vide .muted { flex: none; }

tr.mois-attendu > td { background: color-mix(in oklab, var(--warning) 9%, transparent); }
tr.mois-attendu > td.sticky-col { box-shadow: inset 3px 0 0 var(--warning); }
.marque-attendu { margin-left: 6px; color: var(--warning); font-size: var(--font-sm); }
.note-relance { background: color-mix(in oklab, var(--warning) 12%, var(--surface-1));
  border-color: color-mix(in oklab, var(--warning) 38%, transparent);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.note-relance .btn { margin-left: auto; flex: none; }

.card.plan { border-color: color-mix(in oklab, var(--accent) 30%, var(--border)); }
.plan-phrase {
  margin: 0 0 14px; font-size: var(--font-base); line-height: 1.65;
  color: var(--text-primary);
}
.plan-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 18px; }
.plan-cols h3 {
  margin: 0 0 6px; font-size: var(--font-xs); font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; color: var(--muted);
}
.plan-cols ul { margin: 0; padding-left: 16px; line-height: 1.85; font-size: var(--font-md); }

.perimetre {
  margin: 0 0 12px; font-size: var(--font-sm); line-height: 1.6;
  color: var(--text-secondary);
}
.perimetre.exclues { margin-top: -6px; }
.perimetre-tete {
  margin: 0; padding: 2px 0 2px 12px;
  border-left: 2px solid color-mix(in oklab, var(--accent) 55%, transparent);
}
.perimetre .mois-lien { font-size: var(--font-sm); }
/* Un lien pose dans une precision de pied de liste : il herite de la taille et
   de la couleur du `.sub` qui le porte, sinon un bouton de 13 px surgit au
   milieu d'un texte de 11 px. */
dt .sub .mois-lien { font: inherit; color: inherit; }
.reeq { list-style: none; margin: 4px 0 0; padding: 0; }
.reeq-groupe {
  font-size: var(--font-xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); padding: 2px 0 6px;
}
.reeq-groupe-suite { margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--border-strong, var(--border)); }
.reeq-groupe + .reeq-ligne { padding-top: 2px; }

.reeq-ligne { padding: 12px 0; border-bottom: 1px solid var(--border); }
.reeq-ligne:last-child { border-bottom: none; padding-bottom: 2px; }
.reeq-haut, .reeq-bas { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.reeq-nom { font-weight: 600; min-width: 0; }
.reeq-val { white-space: nowrap; font-variant-numeric: tabular-nums; }
.reeq-droite { flex: none; display: inline-flex; align-items: center; gap: 4px; }
.reeq-droite .btn.icon.xs { padding: 2px 5px; font-size: var(--font-sm); }

.reeq-jauge {
  position: relative; height: 8px; margin: 8px 0 7px;
  border-radius: var(--radius-pill); background: var(--surface-2);
}
/* Une jauge de cible, deux couleurs et non trois.

   Le bleu de `series-1` peignait la barre sous la cible : c'est la teinte des
   liquidites, elle n'avait rien a dire ici. Un degrade de l'accent la remplace —
   c'est la page des objectifs, et le violet y est la couleur de ce qu'on decide.

   Le depassement porte le meme violet. Il a ete rouge, puis ambre, et les deux
   etaient faux pour la meme raison : detenir plus que sa cible n'est pas un
   incident, c'est un ecart a arbitrer, exactement comme en detenir moins. Une
   couleur d'alerte sur la moitie des cas faisait d'une page ou rien ne va mal la
   plus alarmante de l'application.

   Et elle ne servait a rien : le repere de cible est dessine sur la piste, donc
   le cote se lit a la position de la barre, pas a sa teinte. Le vert reste seul
   a dire quelque chose — la cible est atteinte, la seule nouvelle de la carte
   qui merite sa couleur. */
.reeq-reel { position: absolute; left: 0; top: 0; bottom: 0; border-radius: var(--radius-pill); }
.reeq-reel.sous, .reeq-reel.sur {
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--accent) 55%, transparent), var(--accent));
}
.reeq-reel.ok { background: var(--good); }
.reeq-cible {
  position: absolute; top: -3px; bottom: -3px; width: 2px;
  background: var(--text-primary); opacity: .75; border-radius: 2px;
}
.reeq-jauge.ouvrable {
  display: block; width: 100%; border: 0; padding: 0; cursor: pointer;
  -webkit-appearance: none; appearance: none;
  transition: background .15s ease;
}
.reeq-jauge.ouvrable::after {
  content: ''; position: absolute; left: 0; right: 0; top: -7px; bottom: -6px;
}
@media (hover: hover) { .reeq-jauge.ouvrable:hover { background: var(--surface-3, var(--border)); } }
.reeq-jauge.ouvrable:active > .reeq-reel { opacity: .8; }
.reeq-jauge.ouvrable:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
.reeq-bas { font-size: var(--font-sm); }
.reeq-part { display: inline-flex; align-items: baseline; gap: 5px; min-width: 0;
  color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.reeq-ecart { flex: none; white-space: nowrap; font-weight: 600; }
/* Un arbitrage a faire, ni bon ni mauvais : la couleur de la page, pas celle
   d'une alerte. « A la cible » garde le gris de `.muted`, qui dit qu'il n'y a
   rien a lire de plus. */
.reeq-ecart.a-faire { color: var(--text-secondary); }
.reeq-ecart.a-faire b, .montant-plan { color: var(--text-primary); font-weight: 650; }

.reeq-ecart.a-faire b.renfort, .montant-plan.renfort { color: var(--good-text); }
.reeq-pied { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }

/* La cible se choisit dans une liste, plus au clavier : `width: auto` laisse le
   selecteur prendre la largeur de son plus long libelle, « 100 », au lieu des
   3,4 em taillees pour un champ de saisie. */
.cible-champ {
  width: auto; padding: 3px 6px; font-size: var(--font-sm);
  border: 1px solid var(--border-strong); background: var(--surface-1);
  text-align: right; font-variant-numeric: tabular-nums;
}
.cible-champ:focus { border-color: var(--accent); }
@media (hover: hover) { .cible-champ:hover { border-color: var(--accent); } }
.reeq-part .u { color: var(--muted); }

.role-barre {
  margin-top: 9px;
  display: flex; height: 14px; border-radius: var(--radius-pill); overflow: hidden;
  background: var(--surface-2); gap: 2px;
}
.role-part { display: flex; height: 100%; gap: 2px; border-radius: var(--radius-pill); overflow: hidden; }
.role-barre i { display: block; height: 100%; background: var(--c, var(--series-1)); }
.role-barre i.vide { width: 100%; background: var(--surface-2); }
.role-barre i.raye, .role-legende i.raye {
  background-image: repeating-linear-gradient(135deg,
    rgba(0,0,0,.34) 0 3px, transparent 3px 6px);
}
/* La barre d'un role s'ouvre sur ses placements, comme les jauges de cibles.

   Un bouton qui l'enveloppe, et non la barre elle-meme faite bouton : elle
   porte `overflow: hidden` — c'est ce qui decoupe ses segments en pilule — et
   un pseudo-element de zone sensible y serait donc rogne. Le bouton prend les
   marges a son compte : la barre garde ses nueve pixels du dessus, le bouton
   ajoute cinq pixels sous elle et les reprend en marge negative, donc rien ne
   bouge et la zone sensible fait 28 pixels. Les jauges de cibles, plus serrees
   entre leurs voisines, n'en offrent que 21.

   Le survol eclaircit la piste, pas les segments : c'est la piste qui est le
   bouton. Neutre, comme partout ailleurs, le violet restant a ce qu'on decide. */
.role-barre-lien {
  display: block; width: 100%; border: 0; background: none; cursor: pointer;
  padding: 0 0 5px; margin: 0 0 -5px;
  -webkit-appearance: none; appearance: none;
}
.role-barre-lien > .role-barre { transition: background .15s ease; }
@media (hover: hover) { .role-barre-lien:hover > .role-barre { background: var(--surface-3, var(--border)); } }
/* Les segments sont maintenant petits-enfants de la piste, pas enfants : ils
   vivent dans `.role-part`. Un selecteur en enfant direct ne les atteindrait
   plus, et l'appui perdrait son retour visuel. */
.role-barre-lien:active > .role-barre i { opacity: .8; }
.role-barre-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-pill); }
.role-legende { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 5px; font-size: var(--font-sm); color: var(--text-secondary); }
.role-legende span { display: inline-flex; align-items: center; gap: 5px; }
.role-legende i { width: 8px; height: 8px; border-radius: 2px; background: var(--c, var(--series-1)); }

.liste-mobile { display: none; }

/* Une liste qui est la lecture principale, sur tous les ecrans. `.liste-mobile`
   s'efface au-dela de 768 px, parce qu'elle est le repli d'une grille ; celle-ci
   non, parce qu'aucune grille ne l'attend au-dessus — la liste EST l'affichage.
   Les relevés et le journal des ventes la portent. Le nom ne dit plus « releves »
   pour cette raison : la deuxieme carte a en avoir besoin s'est retrouvee devant
   une classe qui parlait de la premiere, et a repris `.liste-mobile`, invisible
   sur un ecran large. */
.liste-principale { display: block; }
tr.ligne-ouvre { cursor: pointer; }
@media (hover: hover) { tr.ligne-ouvre:hover { background: var(--surface-2); } }
tr.ligne-ouvre .mois-lien { pointer-events: none; }
.mlist {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 12px 2px; background: none; border: none;
  border-bottom: 1px solid var(--border);
  font: inherit; color: var(--text-primary); text-align: left; cursor: pointer;
}
.mlist:last-child { border-bottom: none; }
.mlist-groupe { margin-top: 2px; }
.mlist-groupe > .mlist:last-child { border-bottom: none; }
.revoir .revoir-surplus { display: none; }
.revoir.ouvert .revoir-surplus { display: flex; }
.cpt-sans-rien { margin: 10px 4px 0; }
.lien-vue { color: var(--series-1); text-decoration: none; }
@media (hover: hover) { .lien-vue:hover { text-decoration: underline; } }
.lien-vue:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-xs); }
button.lien-vue {
  background: none; border: none; padding: 0; font: inherit; cursor: pointer;
}
/* Le mois en cours se repere dans la liste comme il se repere dans le
   tableau. La regle passe avant `:active`, sinon l'appui n'aurait plus de
   retour visuel sur cette ligne-la, la seule qu'on touche souvent. */
.mlist.mois-courant { background: color-mix(in oklab, var(--accent) 7%, transparent); }
.mlist:active { background: var(--surface-2); }
.ml-nom { flex: 1 1 auto; min-width: 0; font-weight: 600; }
.ml-nom .sub {
  display: block; margin-top: 2px; font-size: var(--font-sm); font-weight: 400;
  color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* LA COLONNE DES CHIFFRES NE PEUT PAS ECRASER LE NOM. En `flex: none`, elle
   prenait toute la largeur que son contenu demandait, et le nom, en
   `min-width: 0`, absorbait le choc : « MSCI World » s'ecrivait sur trois
   lignes derriere un montant a vingt-sept chiffres. Un tel montant n'entre
   plus dans le modele ; mais un patrimoine reel a dix chiffres existe, et la
   ligne doit tenir sans lui non plus. Plafond a soixante pour cent, retour a
   la ligne autorise dans le nombre, et le nom garde au moins ses quarante. */
.ml-chiffres {
  flex: none; min-width: 0; max-width: 60%;
  display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ml-chiffres b { overflow-wrap: anywhere; white-space: normal; text-align: right; }
.ml-chiffres b { font-size: var(--font-base); }
.ml-chiffres span { font-size: var(--font-sm); font-weight: 600; }
/* La jauge de variation du journal des releves : elle occupe l'espace laisse
   libre entre le nom du mois et les montants, et elle n'en prend jamais plus.

   `flex: 0 1` et non `1 1` : le nom du mois porte deja la croissance, et deux
   elements qui poussent se disputeraient la place -- une note un peu longue
   ecrasait alors la jauge, ou l'inverse. Elle a une largeur souhaitee et cede
   quand la ligne se serre.

   Trois pixels de haut, les couleurs deja au vocabulaire de l'application, et
   aucun axe dessine : les barres partent toutes du meme milieu, ce qui suffit a
   le faire voir. */
.ml-jauge {
  flex: 1 1 auto; min-width: 24px; max-width: 260px; position: relative; height: 3px;
}
/* Le nom du mois prend une colonne stable au lieu de toute la place restante.

   Deux raisons, et la seconde decide : sans largeur fixe, « avr. 26 » et
   « mars 26 » donnent deux colonnes de largeurs differentes, donc deux pistes
   qui ne commencent pas au meme endroit -- et le zero implicite, qui est tout ce
   qui rend ces barres lisibles ensemble, disparait. Les centres doivent
   s'aligner d'une ligne a l'autre.

   `flex: 0 0` et non `0 1` : la colonne ne se comprime pas, sinon un mois plus
   long la retrecirait pour lui seul. Le texte qui depasse s'ellipse -- une note
   utilisateur peut etre longue, et elle a deja sa propre regle pour ca. */
.mlist.avec-jauge .ml-nom {
  flex: 0 0 6.5em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mlist.avec-jauge .ml-chiffres { margin-left: auto; }
.mlist.avec-jauge .ml-jauge { margin: 0 auto; }
.ml-jauge i {
  position: absolute; top: 0; height: 100%; border-radius: 2px; min-width: 2px;
}
.ml-jauge i.up { background: var(--good); }
.ml-jauge i.down { background: var(--critical); }
.ml-jauge i.plat {
  left: 50%; width: 3px; margin-left: -1.5px; background: var(--muted); opacity: .55;
}
@media (max-width: 767px) {
  .mlist.avec-jauge .ml-nom { flex: 0 0 6em; }
  .ml-jauge { min-width: 16px; }
}
@media (max-width: 359px) {
  .ml-jauge { display: none; }
}
.ml-chev { flex: none; color: var(--muted); font-size: 18px; line-height: 1; }
.repart-haut .ml-chev { margin-left: -2px; font-size: var(--font-lg); }

@media (max-width: 767px) {
  .liste-mobile { display: block; }
  .large-seulement { display: none; }
}

/* ---------------- Flux du budget ----------------
   Les barres des categories d'un mois, dans la carte de saisie. Elles ont servi
   un temps aux deux cartes du haut de Budget, et c'est precisement ce qui les
   faisait se ressembler : un meme composant pour deux questions. Le partage du
   revenu a maintenant sa forme, juste en dessous.
   `.flow-lien` etait parti avec : plus une seule de ces barres ne menait
   quelque part. `.flow-pct` revient, dans le panneau qui montre tous les postes
   — la quatrieme colonne de la grille l'attendait deja. */
.flow { display: flex; flex-direction: column; gap: 10px; }
/* La colonne du pourcentage n'existe que si un pourcentage l'occupe.

   `:has()` plutôt qu'une classe de variante : la mise en page suit ce que la
   ligne contient, et la prochaine carte qui emploiera cette grille n'aura pas à
   se souvenir d'un modificateur. C'est le mécanisme déjà retenu pour le bandeau
   d'établissement, qui se pose sous les sous-onglets quand la page en a. */
.flow-row { display: grid; grid-template-columns: 118px minmax(0, 1fr) auto; align-items: center; gap: 10px; }
.flow-row:has(.flow-pct) { grid-template-columns: 118px minmax(0, 1fr) auto 52px; }
.flow-label { font-size: var(--font-sm); color: var(--text-secondary); }
.flow-bar { height: 9px; border-radius: var(--radius-pill); background: var(--surface-2); overflow: hidden; }
.flow-bar div { height: 100%; border-radius: var(--radius-pill); }
.flow-val { font-size: var(--font-md); font-variant-numeric: tabular-nums; }
.flow-pct { font-size: var(--font-sm); color: var(--muted); font-variant-numeric: tabular-nums; text-align: right; }

/* Une ligne qui porte son cout annuel doit se donner a toucher.

   Le selecteur vise `[data-aide]` et non `.flow-row` : la carte du Budget emploie
   la meme grille pour six lignes qui, elles, ouvrent le panneau au clic. Deux
   comportements sur une meme classe demandent deux selecteurs, sinon la premiere
   evolution de l'un deteint sur l'autre.

   La zone deborde de 6 px de chaque cote sans decaler la grille — marge negative
   contre remplissage — parce qu'une rangee de 9 px de barre est plus fine que le
   doigt qui la vise. Le survol reste sous `hover: hover` : au doigt il ne se leve
   pas, et la ligne resterait allumee apres l'appui. */
.flow-row[data-aide] {
  cursor: pointer; border-radius: var(--radius-sm);
  margin: -3px -6px; padding: 3px 6px;
  transition: background-color .12s;
}
@media (hover: hover) {
  .flow-row[data-aide]:hover { background: var(--surface-2); }
}
.flow-row[data-aide]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; background: var(--surface-2);
}

.sans-veuve { white-space: nowrap; }

.bascules { display: flex; flex-direction: column; }
.bascule {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 12px 2px; background: none; border: none;
  border-bottom: 1px solid var(--border);
  font: inherit; color: var(--text-primary); text-align: left; cursor: pointer;
}
.bascule:last-child { border-bottom: none; }
.bascule:active { background: var(--surface-2); }
.bascule-txt { flex: 1 1 auto; min-width: 0; font-weight: 600; }
.bascule-txt .sub {
  display: block; font-weight: 400; font-size: var(--font-sm); color: var(--muted);
  margin-top: 2px;
}
/* La piste : grise eteinte, accent allumee. Le pouce glisse en `transform`,
   jamais en `left` — la composition reste sur le GPU. */
.bascule-piste {
  flex: none; position: relative; width: 42px; height: 25px;
  border-radius: var(--radius-pill); background: var(--surface-2);
  border: 1px solid var(--border-strong);
  transition: background .2s ease, border-color .2s ease;
}
.bascule-piste i {
  position: absolute; top: 2px; left: 2px; width: 19px; height: 19px;
  border-radius: 50%; background: var(--text-secondary);
  transition: transform .22s cubic-bezier(.34, 1.28, .48, 1), background .2s ease;
}
.bascule.on .bascule-piste {
  background: var(--accent);
  border-color: color-mix(in oklab, var(--accent) 70%, transparent);
}
.bascule.on .bascule-piste i { transform: translateX(17px); background: #fff; }
.bascule:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
@media (prefers-reduced-motion: reduce) {
  .bascule-piste, .bascule-piste i { transition: none; }
}

.flow-lien {
  display: block; width: 100%; text-align: left;
  background: none; border: none; padding: 0; margin: 0; font: inherit; color: inherit;
  cursor: pointer; border-radius: var(--radius-sm);
}
@media (hover: hover) { .flow-lien:hover .flow-val { color: var(--accent); } }
.flow-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

.flux-total {
  display: flex; align-items: baseline; gap: 8px; width: 100%;
  background: none; border: none; padding: 2px 0 12px;
  font: inherit; color: var(--text-primary); text-align: left; cursor: pointer;
}
.flux-total b { font-size: var(--font-2xl); font-weight: var(--fw-chiffre); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.flux-total span { font-size: var(--font-sm); }
.flux-chev { margin-left: auto; color: var(--muted); font-size: 18px; line-height: 1; }
@media (hover: hover) { .flux-total:hover b { color: var(--accent); } }
@media (hover: hover) { .flux-total:hover .flux-chev { color: var(--text-secondary); } }
.flux-total:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-xs); }

.flux-legende { display: flex; flex-direction: column; margin-top: 4px; }
/* La regle vient apres `.repart-haut` a dessein : meme composant, ecart plus
   serre ici, et c'est l'ordre qui tranche a specificite proche. */
.flux-legende .repart-haut {
  padding: 9px 0; border-bottom: 1px solid var(--border); width: 100%;
}
.flux-legende > :last-child { border-bottom: none; }
.flux-lien {
  background: none; border: none; font: inherit; color: inherit;
  text-align: left; cursor: pointer;
}
@media (hover: hover) { .flux-lien:hover .repart-nom { color: var(--accent); } }
.flux-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

.table-wrap { overflow-x: auto; margin: 0 -4px; padding: 0 4px; }
table { width: 100%; border-collapse: collapse; font-size: var(--font-md); }
th {
  text-align: right; font-weight: 600; font-size: var(--font-xs); text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); padding: 0 10px 9px; white-space: nowrap; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--surface-1); z-index: 1;
}
th:first-child, td:first-child { text-align: left; }
td { padding: 9px 10px; border-bottom: 1px solid var(--grid); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (hover: hover) { tbody tr:hover td { background: color-mix(in oklab, var(--surface-2) 70%, transparent); } }

tr.jalon-retenu td:first-child,
tr.jalon-retenu td:first-child .muted { color: var(--accent); }
tr.jalon-retenu td:first-child { font-weight: 650; }
tbody tr:last-child td { border-bottom: none; }
tfoot td { font-weight: 650; border-top: 1px solid var(--border-strong); border-bottom: none; padding-top: 11px; }
tfoot tr.pied-note td {
  font-weight: 400; border-top: none; padding-top: 2px; text-align: left;
  font-size: var(--font-xs); color: var(--muted); white-space: normal;
}
/* Une cellule qui porte un nom porte du texte, pas un nombre : elle s'aligne a
   gauche. `td` est a droite par defaut, ce qui est juste pour des montants, et
   seule la premiere colonne repassait a gauche. Toutes les cellules `.name`
   etaient premieres sauf une, la note du mois, qui se retrouvait donc alignee
   comme un chiffre : on lisait le debut de la phrase et la fin sortait du cadre. */
td.name { font-weight: 550; text-align: left; }

/* La note d'un mois est de la prose, et la seule du tableau : elle se replie sur
   plusieurs lignes au lieu d'etre coupee. `td` est en `nowrap` parce qu'un montant
   ne se coupe pas en deux ; une phrase, si. La borne haute evite qu'une note
   bavarde pousse les colonnes de chiffres hors de l'ecran. */
td.prose {
  white-space: normal; text-align: left;
  min-width: 16em; max-width: 26em; overflow-wrap: anywhere;
}
th.prose { text-align: left; }

/* Pastille de couleur devant un libelle : elle rattache la ligne du tableau a
   sa part du graphique juste au-dessus. En `inline-block` avec une marge a
   droite plutot qu'en flex, pour que le libelle continue de se replier
   normalement sur une cellule etroite. Le leger halo la detache d'un fond
   sombre sans lui donner de contour franc. */
.teinte {
  display: inline-block; width: 9px; height: 9px; border-radius: 3px;
  margin-right: 8px; flex-shrink: 0; vertical-align: baseline;
  background: var(--c, var(--muted));
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--c, var(--muted)) 35%, transparent);
}
td.name .teinte ~ .sub { padding-left: 17px; }
td .sub, dt .sub { display: block; font-size: var(--font-xs); color: var(--muted); font-weight: 400; }

.table-portefeuille .pf-replie,
.table-portefeuille .pf-sous,
.table-portefeuille .pf-voir-moins { display: none; }
.table-portefeuille.tout-voir .pf-replie,
.table-portefeuille.autres-ouvert .pf-sous { display: table-row; }
.table-portefeuille.tout-voir .pf-voir-moins { display: inline; }
.table-portefeuille.tout-voir .pf-voir-tout,
.table-portefeuille.tout-voir .pf-somme { display: none; }
.table-portefeuille .pf-plus .btn { margin: 0; white-space: nowrap; }
.table-portefeuille .pf-sous td { font-size: var(--font-sm); }
.table-portefeuille .pf-sous td.name { padding-left: 17px; font-weight: 400; color: var(--text-secondary); }
.pf-bascule {
  display: flex; align-items: baseline; width: 100%; padding: 4px 0;
  background: none; border: 0; font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.pf-bascule-texte { flex: 1 1 auto; min-width: 0; }
.table-portefeuille .pf-bascule .sub { overflow-wrap: normal; }
.pf-chev { display: inline-block; margin-left: 4px; color: var(--muted); transition: transform .15s; }
.pf-bascule[aria-expanded="true"] .pf-chev { transform: rotate(180deg); }
.pf-bascule:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
@media (hover: hover) { .pf-bascule:hover .pf-bascule-texte { color: var(--accent); } }
@media (prefers-reduced-motion: reduce) { .pf-chev { transition: none; } }
.tag {
  display: inline-block; padding: 2px 8px; border-radius: var(--radius-pill); font-size: var(--font-xs); font-weight: 600;
  background: var(--surface-2); color: var(--text-secondary); border: 1px solid var(--border); letter-spacing: .01em;
  white-space: nowrap; flex: none;
}
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 3px; margin-right: 7px; vertical-align: baseline; }

table.editable td { padding: 4px 5px; }
table.editable input, table.editable select {
  padding: 5px 7px; font-size: var(--font-sm); border-color: transparent; background: transparent;
  border-bottom: 1px dashed color-mix(in oklab, currentColor 38%, transparent);
}
table.editable input.derived { border-bottom-color: transparent; }
@media (hover: hover) { table.editable input:hover, table.editable select:hover { border-color: var(--border-strong); background: var(--surface-1); } }
table.editable input:focus, table.editable select:focus { background: var(--surface-1); }
table.editable td.name input { text-align: left; font-weight: 550; }

table.editable select.rev-periode {
  display: block; width: auto; max-width: 100%; margin: 4px 0 0 auto;
}
table.editable .rev-estime {
  display: flex; align-items: center; gap: 6px; cursor: pointer; margin-top: 4px;
}
table.editable .rev-estime input[type="checkbox"] {
  width: auto; margin: 0; padding: 0; border: none; accent-color: var(--accent);
}
.rev-liste { display: flex; flex-direction: column; gap: 14px; }
.rev-source {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 12px; background: var(--surface-2);
}
.rev-tete { display: flex; align-items: flex-end; gap: 8px; }
.rev-nom { flex: 1; min-width: 0; }
.rev-suppr { flex: none; margin-bottom: 2px; }
.rev-champs {
  display: grid; gap: 10px; margin-top: 10px;
  grid-template-columns: repeat(auto-fit, minmax(9em, 1fr));
}
.rev-champs .field select, .rev-champs .field input { width: 100%; }
.rev-pied {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 10px; font-size: var(--font-sm);
}
.rev-estime { display: flex; align-items: center; gap: 6px; cursor: pointer; color: var(--muted); }
.rev-estime input[type="checkbox"] {
  width: auto; margin: 0; padding: 0; border: none; accent-color: var(--accent);
}
.rev-total {
  display: flex; align-items: baseline; gap: 10px; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--grid);
}
.rev-total b { margin-left: auto; font-size: var(--font-lg); font-variant-numeric: tabular-nums; }

table.editable input.derived { color: var(--text-secondary); font-size: var(--font-sm); }
table.editable input.bad { border-color: var(--critical); background: color-mix(in oklab, var(--critical) 10%, transparent); }
.sym-cell { white-space: nowrap; }
.sym-cell input { width: calc(100% - 26px); display: inline-block; }
.sym-cell input:only-child { width: 100%; }
.btn.icon.xs { padding: 2px 5px; font-size: var(--font-sm); vertical-align: middle; }
@media (hover: hover) { .btn.icon.xs:hover { color: var(--accent); } }

.sticky-col { position: sticky; left: 0; background: var(--surface-1); z-index: 2; }
@media (hover: hover) { tbody tr:hover .sticky-col { background: color-mix(in oklab, var(--surface-2) 92%, var(--surface-1)); } }

.chart { position: relative; width: 100%; }
/* `overflow: visible` laisse les etiquettes d'axe deborder du viewBox, ce
   qui est voulu. Mais il laissait aussi une serie hors echelle peindre par
   dessus les cartes suivantes : les series sont donc decoupees dans le SVG
   lui-meme, et le conteneur borne les degats. */
/* Le SVG du graphique lui-meme, pas n'importe quel SVG a l'interieur : les
   infobulles vivent dans `.chart` et peuvent contenir un montant masque.
   En selecteur descendant, cette regle etirait le petit oeil a 100 % de la
   largeur de la carte. */
.chart > svg { display: block; width: 100%; height: auto; overflow: visible; }
.chart { contain: paint; }

/* Le graphique n'a plus sa garde a lui : elle vivait ici en trois regles, dont
   la derniere RENDAIT la selection aux paragraphes et aux tableaux de la carte —
   c'est-a-dire aux montants, aux titres et aux textes d'aide, exactement ce qui
   ne doit plus se selectionner. La regle de `body`, en tete de fichier, couvre le
   dessin, ses etiquettes d'axe et la carte qui le porte, sans exception a lever
   ni ancetre a remonter. */
.chart text { font-family: var(--font); }
.tick { font-size: var(--font-xs); fill: var(--muted); font-variant-numeric: tabular-nums; }
.tick-strong { fill: var(--text-secondary); font-weight: 600; }
.rb-label { font-size: var(--font-sm); fill: var(--text-primary); dominant-baseline: middle; }
.rb-val { font-size: var(--font-sm); fill: var(--text-primary); dominant-baseline: middle; font-variant-numeric: tabular-nums; font-weight: 550; }
.rb-pct { fill: var(--muted); font-weight: 400; }
@media (hover: hover) { .rb:hover .rb-label { fill: var(--accent); } }
.donut-val { font-size: var(--font-xl); font-weight: 650; fill: var(--text-primary); letter-spacing: -.02em; }
.donut-lab { font-size: var(--font-xs); fill: var(--muted); }
.slice { transition: opacity .12s; cursor: default; }
@media (hover: hover) { .chart:hover .slice { opacity: .55; } }
@media (hover: hover) { .chart .slice:hover { opacity: 1; } }

.chart-tip {
  position: absolute; pointer-events: none; z-index: 9;
  background: var(--surface-1); border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 9px 11px; box-shadow: var(--shadow); font-size: var(--font-sm); min-width: 170px;
}
.tt-head { font-weight: 650; margin-bottom: 6px; font-size: var(--font-sm); }
.tt-sous { margin: -4px 0 6px; font-size: var(--font-xs); color: var(--text-secondary); }

.tt-row { display: flex; align-items: center; gap: 7px; padding: 2px 0; color: var(--text-secondary); }
.tt-row b { margin-left: auto; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.tt-total { border-top: 1px solid var(--grid); margin-top: 5px; padding-top: 5px; font-weight: 600; }
.tt-note { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--grid); color: var(--muted); font-size: var(--font-sm); white-space: normal; max-width: 260px; }
.sw { width: 9px; height: 9px; border-radius: 3px; flex-shrink: 0; }

/* --- La bulle de la sparkline : deux lignes, et rien de plus ----------------

   Elle emprunte tout au reste — le fond, la bordure, l'ombre, l'intitule en
   petites capitales de `tt-head` — et ne change que ce qu'une bulle de deux
   lignes demande : un remplissage plus serre, et aucun plancher de largeur.
   Celui des grandes infobulles, 170 px, la rendait deux fois trop large pour
   « MARS 2026 » et la poussait contre les bords sans raison.

   `tabular-nums` SUR LE MONTANT, ET CE N'EST PAS UNE COQUETTERIE. Le doigt
   glisse d'un mois a l'autre : sans chiffres de largeur egale, la bulle change
   de largeur a chaque point et se recale, donc elle saute lateralement sous le
   doigt qui la lit. */
.tip-spark {
  padding: 5px 9px; min-width: 0; line-height: 1.25;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.tip-spark .tt-head { margin-bottom: 1px; }
.tip-spark b { font-variant-numeric: tabular-nums; }

/* --- L'infobulle de l'historique, en trois colonnes ------------------------

   Libelle, montant, poids. Les trois colonnes s'alignent d'une ligne a l'autre,
   ce qu'une rangee en flex ne sait pas faire : chaque rangee y est son propre
   conteneur, donc sa colonne de montants se cale sur la longueur de son propre
   libelle, et rien ne s'aligne avec la rangee du dessus. Une seule grille pour
   toute la bulle, et les rangees s'y fondent par `display: contents`.

   Le filet du total est un element a part, qui traverse les quatre colonnes :
   une rangee fondue ne peut pas porter de bordure, elle n'a plus de boite.

   LE MONTANT RESTE PRIORITAIRE. Il garde l'encre pleine et la graisse ; le poids
   passe en encre secondaire, a la meme taille. Le reduire aurait fait une
   troisieme information a dechiffrer plutot qu'a lire du coin de l'oeil. */
.tt-parts {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: baseline; column-gap: 7px; row-gap: 4px; color: var(--text-secondary);
}
.tt-ligne { display: contents; }
.tt-parts .sw { align-self: center; }
.tt-parts b { color: var(--text-primary); font-variant-numeric: tabular-nums; text-align: right; }
/* `tabular-nums` des deux cotes : sans lui, « 1 » et « 4 » n'ont pas la meme
   largeur et les virgules des poids dansent d'une ligne a l'autre.
   `nowrap` : « 23,2 % » ne se coupe pas devant son signe. */
.tt-poids {
  color: var(--muted); font-variant-numeric: tabular-nums;
  text-align: right; white-space: nowrap;
}
.tt-filet { grid-column: 1 / -1; border-top: 1px solid var(--grid); margin-top: 1px; }
.tt-fin { font-weight: 600; }

.legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 10px; font-size: var(--font-sm); color: var(--text-secondary); }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.legend i.legend-bande {
  width: 16px; height: 0; border-radius: 0;
  border-top: 1px dashed var(--muted);
}

.modal {
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center; padding: 20px;
  background: rgba(0,0,0,.55); backdrop-filter: blur(3px);
  animation: modal-fond .15s ease;
}
/* Pas de flou derrière la fenêtre sur téléphone.

   `backdrop-filter` demande au compositeur de re-flouter tout ce qui se trouve
   derrière, **à chaque image**, pendant que le panneau se déplace au-dessus.
   Sur un écran de bureau ça passe ; sur un iPhone, le fond à flouter est une
   page de trois mille pixels que Safari doit d'abord rasteriser. La première
   ouverture trouve ces couches déjà prêtes — la page n'a pas bougé depuis un
   moment — les suivantes arrivent après un `render()` qui a reconstruit toute
   la vue et ses graphiques, donc tout est à repeindre. C'est exactement la
   forme du symptôme.

   Ce qu'on perd est presque rien : sous un voile noir à 55 %, trois pixels de
   flou ne se voient pas. Ce qu'on garde, c'est l'assombrissement, qui fait tout
   le travail de séparation.

   Même raisonnement que pour l'ovale du bas, dont `ETAT.md` note déjà qu'il est
   le suspect no 1 des accrocs pendant un geste : un flou et une animation ne
   doivent pas se disputer le même compositeur. */
@media (max-width: 900px) {
  .modal { backdrop-filter: none; -webkit-backdrop-filter: none; }
}
.modal[hidden] { display: none; }
:root {
  --duree-fenetre: .34s;
  --duree-fenetre-sortie: .26s;
}

@keyframes modal-fond { from { opacity: 0 } to { opacity: 1 } }
@keyframes modal-entree { from { opacity: 0; transform: translate3d(0, 10px, 0) scale(.985) } to { opacity: 1; transform: none } }

.modal-panel {
  width: min(30em, 100%);
  /* Voir la note sur `dvh` dans le bloc telephone plus bas : `vh` ignore la
     barre d'adresse, `dvh` la suit. */
  max-height: min(80vh, 640px);
  max-height: min(80dvh, 640px);
  /* La fenetre est une grille, et un element de grille recoit `min-width:
     auto` : il refuse de descendre sous la largeur minimale de son contenu,
     et cette regle bat `width: 100%` comme `max-width: 100%`. Sur un ecran de
     375 px, le panneau s'etalait a 399 px des qu'une fiche portait un contenu
     un peu large — le tableau, les valeurs, tout finissait 5 px au-dela du
     bord, hors d'atteinte puisque la page coupe ce qui depasse. Le contenu se
     replie desormais, comme il aurait toujours du. */
  min-width: 0;
  display: flex; flex-direction: column;
  background: var(--surface-1); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--ombre-fenetre);
  animation: modal-entree var(--duree-fenetre) cubic-bezier(.22, .68, .28, 1);
  /* Le panneau annonce ce qu'il va animer : Safari lui donne alors sa propre
     couche avant la premiere image, au lieu de la creer en cours de route — ce
     qui coute une trame et se voit. Il ne vit que le temps d'une fenetre
     ouverte, donc la couche ne survit a rien.
     `translate3d` plutot que `translateY` dans les images-cles, pour la meme
     raison : c'est le vieux remede des animations qui accrochent sur iOS, et ce
     fichier l'applique deja aux bandes collantes du haut. */
  will-change: transform, opacity;
}
/* L'en-tete d'une fiche ne se selectionne pas — pas plus que son corps depuis
   que la regle de `body` couvre l'application entiere. Les trois declarations
   qui vivaient ici ne disaient plus rien de neuf ; seuls les champs de saisie du
   corps gardent leur selection, et c'est l'exception d'en-tete de fichier qui le
   dit. */
.modal-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 16px 18px 12px; border-bottom: 1px solid var(--grid);
}
.modal-head h2 { margin: 0; font-size: var(--font-lg); letter-spacing: -.01em; }
.modal-sub { margin: 3px 0 0; font-size: var(--font-sm); color: var(--muted); }
.modal-body {
  padding: 6px 18px 14px; overflow-y: auto; overscroll-behavior: contain;
  /* Defilement inertiel a l'interieur d'une couche `fixed`. Sur iOS, un
     conteneur defilant place dans un calque fixe, pendant que `body` porte
     `overflow: hidden`, peut refuser le geste : c'est le vieux defaut du
     verrouillage de defilement. Cette ligne ne coute rien la ou tout va bien et
     debloque le cas ou ca coince. */
  -webkit-overflow-scrolling: touch;
}
/* Un glissement qui part d'un champ doit defiler la fiche, pas deplacer le
   curseur. La fiche d'une ligne de marche est presque entierement faite de
   champs : sans cette declaration, le navigateur peut lire le geste comme une
   selection de texte et la fiche parait bloquee. `pan-y` lui dit que la
   verticale appartient au conteneur. */
.modal-body :is(input, select, textarea) { touch-action: pan-y; }
.modal-foot {
  padding: 12px 18px 16px; border-top: 1px solid var(--grid);
  display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap;
}
.fiche-danger {
  margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--grid);
}
.fiche-danger .hint { margin: 8px 0 0; }

.modal-champs { display: flex; flex-direction: column; gap: 10px; padding: 12px 0 4px; }
.modal-champs.champs-cote { flex-direction: row; gap: 10px; }
.modal-champs.champs-cote > .field { flex: 1; min-width: 0; }
.modal-champs.champs-cote { align-items: flex-end; }
.modal-champs input.lecture {
  color: var(--text-secondary); background: var(--surface-2);
  border-style: dashed; cursor: default;
}
.modal-champs.champs-cote input { max-width: 100%; }
.champ-inline { max-width: 8.5em; text-align: right; margin-left: auto; display: block; }
/* L'unite d'un champ de saisie, collee a lui. `margin-left: auto` sur le champ
   pousse deja le couple a droite : le symbole ne doit donc pas en reprendre un,
   sinon il partirait seul au bord. Discret comme une unite, jamais comme une
   valeur — c'est le nombre qu'on lit, pas la monnaie. */
.champ-unite { color: var(--muted); font-size: var(--font-sm); margin-left: 6px; flex: none; }
.champ-large { max-width: 11em; margin-left: 0; }
.champ-court { max-width: 17em; }

.plafond { margin-top: 8px; max-width: 22em; }
.plafond-jauge {
  height: 5px; border-radius: var(--radius-pill); background: var(--surface-2);
  overflow: hidden; margin-bottom: 5px;
}
.plafond-jauge > i { display: block; height: 100%; background: var(--series-1); }
.plafond.plein .plafond-jauge > i { background: var(--warning); }

.table-compacte { max-width: 38em; }

.card-link {
  font: inherit; text-align: left; color: inherit; cursor: pointer; position: relative;
  transition: transform .12s, border-color .12s;
}
@media (hover: hover) { .card-link:hover { transform: translateY(-1px); border-color: var(--border-strong); } }
.card-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.card-link .t-go { top: 15px; right: 16px; }

.card-cliquable { position: relative; }
.card-couvre {
  position: absolute; inset: 0; width: 100%; padding: 0;
  background: none; border: none; border-radius: var(--radius); cursor: pointer;
}
@media (hover: hover) { .card-couvre:hover { background: color-mix(in oklab, var(--surface-2) 50%, transparent); } }
.card-couvre:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.card-cliquable > *:not(.card-couvre) { position: relative; pointer-events: none; }
.card-cliquable :is(.aide, .val-masquable, .segmented, .plage, .btn-oeil,
                    .hero-delta-reglable, .rappel-sorties,
                    input, select, textarea) { pointer-events: auto; }

/* Un des trois ecarts du haut se regle : celui de l'objectif de depenses.

   Il doit se distinguer des deux autres, qui ne sont que des lectures, sans
   pour autant se deguiser en bouton — trois blocs alignes dont un seul serait
   encadre casserait la ligne. Un pointille sous le montant suffit : c'est le
   signe qu'on peut le toucher, et il ne prend pas de place.

   Il herite de `.hero-delta` pour sa mise en page, et remet a zero ce qu'un
   <button> apporte de son cote : la bordure, le fond, l'alignement centre et la
   police du systeme. Sans ce dernier point, « Objectif mensuel » ne serait pas
   dans la meme fonte que « Ecart » juste a cote. */
.hero-delta-reglable {
  border: 0; background: none; padding: 0; margin: 0; font: inherit;
  color: inherit; text-align: left; cursor: pointer;
}
.hero-delta-reglable > span { color: var(--accent); }
.hero-delta-reglable > b {
  color: var(--accent);
  border-bottom: 1px dashed color-mix(in oklab, var(--accent) 55%, transparent);
  text-align: center;
  display: inline-flex; align-items: baseline; gap: 3px; justify-content: center;
}
.hero-delta-reglable .hero-delta-chev {
  font-size: 12px; font-weight: 600; opacity: .75; line-height: 1;
}
@media (hover: hover) { .hero-delta-reglable:hover > b { border-bottom-color: var(--accent); } }
.hero-delta-reglable:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius-xs);
}

/* `.champ-objectif` vivait ici : un champ discret, souligne d'un pointille, pose
   parmi les trois ecarts du haut de la brique des depenses. Il etait joli et il
   ne se trouvait pas — un reglage a besoin de son intitule, pas d'etre pres du
   regard. L'objectif est redevenu un champ etiquete au bas de la carte, comme
   dans l'onglet Charges fixes, et cette regle est partie avec lui. */

.modal-panel::before { content: none; }
@media (max-width: 900px) {
  .modal-panel { touch-action: pan-y; }
  .modal-panel::before {
    content: ''; display: block; width: 44px; height: 4px; border-radius: var(--radius-pill);
    background: var(--border-strong, var(--border));
    margin: 9px auto 0; padding: 0; flex: none;
    box-sizing: content-box; border-top: 9px solid transparent;
    border-bottom: 9px solid transparent; background-clip: content-box;
  }
}

.cpt-entete { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }

.cat-retiree td { opacity: .62; }
.cat-retiree .tag { margin-left: 8px; }
/* Les deux gestes d'une categorie tiennent dans une cellule : en colonnes
   separees, le tableau debordait de son cadre a 375 px et la croix passait hors
   de l'ecran. `nowrap` pour qu'ils restent cote a cote plutot que de s'empiler. */
.cat-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; white-space: nowrap; }

/* Le nom d'une fiche — compte ou etablissement — en titre plutot qu'en champ
   de saisie. Il se glisse entre l'etiquette de nature et le montant : plus gros
   que la premiere, plus discret que le second, parce que c'est le sujet de la
   page et non sa reponse. Un `h2` et non un `div` : il porte la police des
   titres comme tous les autres, et c'est bien un titre au sens du document.

   Une seule classe pour les deux fiches. Deux auraient diverge le jour ou l'une
   change de taille. */
.fiche-nom {
  display: flex; align-items: center; gap: 8px;
  margin: 2px 0 0; font-size: var(--font-xl); font-weight: 680; letter-spacing: -.02em;
  color: var(--text-primary);
}

.lien-etab { margin-top: 12px; display: inline-flex; align-items: center; gap: 8px; }

.cpt-resume { margin: 8px 0 12px; padding: 0 4px; }
.cpt-net { font-size: clamp(26px, 4vw, 34px); font-weight: 700; letter-spacing: -.03em; margin: 4px 0 2px; font-variant-numeric: tabular-nums; }
.cpt-entete .date-valeur { margin: 4px 0 0; }
.cpt-entete > .cpt-retrait { flex-basis: 100%; margin: 0; }

.cpt-liste { display: flex; flex-direction: column; gap: 14px; }
.cpt-groupe { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius); overflow: clip; }

.cpt-gtitre {
  display: flex; align-items: center; gap: 10px;
  background: linear-gradient(to right,
    color-mix(in oklab, var(--teinte) 16%, var(--surface-2)),
    var(--surface-2) 65%);
  padding: 0 12px 0 0;
  /* Sous la barre du haut, en la lisant : `72px` ecrit ici la supposait plus
     basse qu'elle n'est, et le bandeau se collait derriere elle. */
  position: sticky; top: var(--h-entete); z-index: 5;
  border-bottom: 1px solid var(--border);
  box-shadow: inset 3px 0 0 var(--teinte);
}
.cpt-pastille {
  flex: none; width: 9px; height: 9px; border-radius: 3px;
  background: var(--teinte);
}
.cpt-gplier {
  display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
  background: none; border: none; cursor: pointer;
  padding: 13px 0 13px 14px; font: inherit; color: var(--text-primary); text-align: left;
}
.cpt-corps { box-shadow: inset 3px 0 0 color-mix(in oklab, var(--teinte) 38%, transparent); }

/*    Le pli anime : une grille dont la rangee passe de 0fr a 1fr. C'est la seule
   facon d'animer une hauteur inconnue sans la mesurer — `height: auto` ne se
   transitionne pas. La visibilite part APRES le mouvement (delai en fermant,
   aucun en ouvrant) : elle sort le contenu replie du clavier et des lecteurs,
   le role que tenait l'attribut hidden quand le pli etait sec.*/
.cpt-pli {
  display: grid; grid-template-rows: 0fr; visibility: hidden;
  transition: grid-template-rows .28s cubic-bezier(.22, .61, .36, 1), visibility 0s .28s;
}
.cpt-pli.ouvert {
  grid-template-rows: 1fr; visibility: visible;
  transition: grid-template-rows .28s cubic-bezier(.22, .61, .36, 1), visibility 0s;
}
/* `min-height: 0` : sans lui, le contenu impose sa hauteur minimale a la
   rangee et la grille ne ferme jamais tout a fait. */
.cpt-pli > .cpt-corps { overflow: hidden; min-height: 0; }
@media (prefers-reduced-motion: reduce) {
  .cpt-pli, .cpt-pli.ouvert { transition: none; }
}
.cpt-gnom { font-size: var(--font-lg); font-weight: 700; letter-spacing: -.01em; display: flex; flex-direction: column; }
.cpt-gnom .sub { font-weight: 500; }
.cpt-gtotal { margin-left: auto; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; display: flex; flex-direction: column; }
.cpt-gtotal .sub { font-weight: 500; }
.cpt-chev { color: var(--muted); font-size: 15px; flex: none; }
.cpt-groupe.replie .cpt-gtitre { border-bottom: none; }

.sub { display: block; font-size: var(--font-xs); color: var(--muted); font-weight: 500; }

.cpt-swipe { position: relative; overflow: hidden; touch-action: pan-y; }
.cpt-actions {
  position: absolute; inset: 0 0 0 auto; display: flex; align-items: center; gap: 6px;
  padding: 0 10px; background: var(--surface-2);
}
.cpt-ligne {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 12px; width: 100%;
  background: var(--surface-1); border: none; border-bottom: 1px solid var(--border);
  padding: 12px 16px; font: inherit; color: var(--text-primary); text-align: left; cursor: pointer;
}
.cpt-swipe:last-child .cpt-ligne { border-bottom: none; }
@media (hover: hover) { .cpt-ligne:hover { background: var(--surface-2); } }
/* `break-word`, jamais `anywhere` : cette derniere autorise la colonne a se
   reduire a une lettre, et le nom s'imprime a la verticale des que la ligne
   porte aussi un champ de saisie. Le plancher garantit un mot lisible. */
.cpt-nom { min-width: 6em; flex: 1; font-weight: 600; overflow-wrap: break-word; }
.plc-ligne { flex-wrap: wrap; }
.plc-ligne .champ-inline { max-width: 7.5em; }
.cpt-val { text-align: right; font-weight: 650; font-variant-numeric: tabular-nums; flex: none; }

.plc-ligne {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px; border-bottom: 1px solid var(--border);
  font-size: var(--font-md);
}
.plc-ligne:last-child { border-bottom: none; }
.card > .plc-ligne { padding-left: 0; padding-right: 0; }

.plc-placement {
  display: grid; grid-template-columns: minmax(0, 1fr) 12em 8em 0.9em;
  align-items: center; gap: 10px; flex-wrap: nowrap;
}
/* `max-width` autant que `min-width` : une piste de grille fixe ne retient
   pas un contenu plus large que lui. Sans ces deux bornes, la liste
   deroulante « Auto, Bloqué » gardait sa largeur naturelle et venait couvrir
   le montant a sa droite. */
.plc-placement .plc-dispo {
  justify-self: stretch; min-width: 0; max-width: 100%;
}
.dispo-reglable { position: relative; display: inline-block; max-width: 100%; }
.dispo-reglable > .tag {
  border-bottom: 1px dashed color-mix(in oklab, currentColor 45%, transparent);
}
.dispo-reglable > select {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; border: 0; padding: 0; margin: 0;
  -webkit-appearance: none; appearance: none;
}
.dispo-reglable:focus-within > .tag {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.plc-placement .plc-dispo .tag {
  display: block; width: fit-content; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis;
}
.plc-placement .cpt-val { justify-self: end; }
.plc-lien {
  width: 100%; font: inherit; font-size: var(--font-md); color: var(--text-primary);
  background: none; border: none; border-bottom: 1px solid var(--border);
  text-align: left; cursor: pointer;
}
.plc-lien:last-child { border-bottom: none; }
@media (hover: hover) { .plc-lien:hover { background: color-mix(in oklab, var(--surface-2) 60%, transparent); } }
.plc-lien:active { background: var(--surface-2); }
.plc-lien .cpt-chev { justify-self: end; }
.plc-entete {
  font-size: var(--font-xs); text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 600; padding-top: 6px; padding-bottom: 6px;
}
@media (max-width: 479px) {
  .plc-placement { grid-template-columns: minmax(0, 1fr) auto; }
  .plc-placement .plc-dispo { grid-column: 1; grid-row: 2; }
  .plc-placement .cpt-val { grid-column: 2; grid-row: 1 / span 2; }
  .plc-entete .plc-dispo { display: none; }
  .plc-entete > span:first-child::after { content: " · disponibilité"; }
}

/* La ligne qui porte un bouton « Modifier » cede sa derniere colonne.

   Les 0.9em du bout etaient tailles pour un chevron. Le bouton y serait entre
   au chausse-pied, alors que la meme rangee sans bouton n'a aucune raison de
   reserver sa largeur : `:has()` fait dependre la grille de son contenu, comme
   ailleurs dans cette feuille pour les pourcentages de flux.

   Ces regles viennent apres celles de `.plc-placement`, y compris apres la
   requete de media a 479 px : meme specificite, c'est l'ordre qui tranche. */
.plc-placement:has(.plc-modif) {
  grid-template-columns: minmax(0, 1fr) 12em 8em auto;
}
.plc-modif { justify-self: end; white-space: nowrap; }
@media (max-width: 479px) {
  .plc-placement:has(.plc-modif) { grid-template-columns: minmax(0, 1fr) auto; }
  .plc-placement:has(.plc-modif) .cpt-val { grid-column: 2; grid-row: 1; }
  .plc-placement:has(.plc-modif) .plc-dispo { grid-column: 1; grid-row: 2; }
  .plc-placement:has(.plc-modif) .plc-modif { grid-column: 2; grid-row: 2; }
}

.tag.mob-immediat { color: var(--good-text); border-color: color-mix(in oklab, var(--good) 40%, transparent); }
.tag.mob-differe  { color: var(--text-secondary); }
.tag.mob-lent     { color: var(--warning); border-color: color-mix(in oklab, var(--warning) 40%, transparent); }
.tag.mob-bloque   { color: var(--serious); border-color: color-mix(in oklab, var(--serious) 40%, transparent); }
.rw-hors { opacity: .72; }

/* Variante sobre du bouton rouge : la couleur passe dans le texte et le
   liseré, pas en aplat. Elle n'etait pas limitee a `.ghost` — a specificite
   egale avec la regle du bouton plein, elle en repeignait le texte en rouge
   sans toucher le fond, lui aussi rouge. « Cloturer et supprimer » dans
   Comptes etait illisible. */
.btn.ghost.danger {
  background: var(--surface-1); color: var(--critical); font-weight: 600;
  border-color: color-mix(in oklab, var(--critical) 75%, transparent);
}
@media (hover: hover) { .btn.ghost.danger:hover {
  background: color-mix(in oklab, var(--critical) 12%, var(--surface-1));
  color: var(--critical);
} }

.repart { padding: 4px 16px; }
.repart-ligne {
  display: flex; flex-direction: column; gap: 5px; width: 100%;
  background: none; border: none; border-bottom: 1px solid var(--border);
  padding: 10px 0; font: inherit; color: var(--text-primary);
  text-align: left; cursor: pointer;
}
.repart-ligne:last-child { border-bottom: none; }
@media (hover: hover) { .repart-ligne:hover .repart-nom { color: var(--accent); } }
.repart-inerte { cursor: default; }

.ptf-lab { margin: 2px 0 0; font-size: var(--font-sm); color: var(--text-secondary); }
.ptf-total {
  margin: 1px 0 0; font-size: var(--font-2xl); font-weight: 700;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.ptf-compo {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 7px 14px; margin: 14px 0 0; padding-top: 12px;
  border-top: 1px solid var(--grid); font-size: var(--font-md);
}
.ptf-compo dt { min-width: 0; color: var(--text-secondary); }
.ptf-compo dd {
  margin: 0; text-align: right; font-weight: 650; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.ptf-compo-lien {
  font: inherit; color: inherit; background: none; border: none; padding: 0;
  text-align: left; cursor: pointer;
}
@media (hover: hover) { .ptf-compo-lien:hover { color: var(--accent); } }

.ptf-pv { margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--grid); }
.ptf-pv-lab { margin: 0; font-size: var(--font-sm); color: var(--text-secondary); }
.ptf-pv-val {
  margin: 2px 0 0; font-size: var(--font-xl); font-weight: 700;
  letter-spacing: -.01em; font-variant-numeric: tabular-nums;
}
.ptf-pv-val.up { color: var(--good-text); }
.ptf-pv-val.down { color: var(--critical); }
.ptf-pv-val.flat, .ptf-pv-val.muted { color: var(--text-secondary); }
.ptf-pv-sec { margin: 1px 0 0; font-size: var(--font-sm); color: var(--text-secondary); }

@media (min-width: 1000px) {
  .card.ptf {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    column-gap: 40px; align-items: start;
  }
  .card.ptf > .card-head { grid-column: 1 / -1; }
  .card.ptf > .ptf-lab, .card.ptf > .ptf-total { grid-column: 1; }
  .card.ptf > .ptf-compo, .card.ptf > .ptf-pv { grid-column: 2; }
  .card.ptf > .ptf-compo { margin-top: 6px; padding-top: 0; border-top: none; }
}
@media (hover: hover) { .repart-inerte:hover .repart-nom { color: inherit; } }
.repart-haut { display: flex; align-items: baseline; gap: 9px; }
.repart-haut .dot { margin: 0; flex: none; }
.repart-nom { font-weight: 600; min-width: 0; overflow-wrap: break-word; }
.repart-haut b { margin-left: auto; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
/* `tabular-nums` : sans lui, « 1 » et « 4 » n'ont pas la meme largeur et les
   virgules dansent d'une ligne a l'autre. La colonne se parcourt du regard, il
   faut qu'elle tombe droit — c'est deja ce que fait le montant juste a cote. */
.repart-pct {
  color: var(--muted); font-size: var(--font-sm); min-width: 3.6em;
  text-align: right; flex: none; font-variant-numeric: tabular-nums;
}
.repart-barre {
  display: block; height: 5px; border-radius: var(--radius-pill);
  background: var(--surface-2); overflow: hidden;
}
.repart-barre i { display: block; height: 100%; border-radius: var(--radius-pill); }
.repart-pied { margin: 4px 0 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.ligne-cible { margin: -4px 0 12px; font-size: var(--font-md); color: var(--text-secondary); }
.ligne-cible b { color: var(--text-primary); }
.repart-base { margin: 12px 0 0; padding-top: 12px; border-top: 1px solid var(--border); }
/* Un montant ne se coupe pas apres la virgule. La regle mobile qui fait
   replier les cellules de tableau — necessaire pour les libelles longs —
   l'emportait sur `nowrap` : ces deux selecteurs sont assez specifiques
   pour la battre, et assez etroits pour ne toucher que des montants. */
.repart-pied dd,
.card .repart-total td:not(:first-child),
.grid > div > table.repart-total td:not(:first-child) { white-space: nowrap; }

.modal-sub.avec-action {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.modal-sub.avec-action > .btn { margin-left: auto; }
.liq-groupe { border-bottom: 1px solid var(--border); }
/* Le sommaire est un bouton et non plus un `<summary>` : `<details>` ne
   s'anime pas, et le pli en grille exige un conteneur qu'on pilote a la
   classe. Il en garde l'allure exacte. */
.liq-sommaire {
  width: 100%; background: none; border: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer; text-align: left;
  display: flex; align-items: center; gap: 10px;
  padding: 11px 0; font-weight: 650;
}
.liq-sommaire[aria-expanded="true"] .cpt-chev { transform: rotate(180deg); }
.liq-corps { overflow: hidden; min-height: 0; }
.liq-sommaire b { margin-left: auto; font-variant-numeric: tabular-nums; }
.liq-groupe .cpt-chev { transition: transform .15s; }
.liq-ligne {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 0 6px 12px; font-size: var(--font-md);
}
.liq-ligne .champ-inline { max-width: 8em; }

@media (max-width: 900px) {
  .cpt-entete .kv { margin-left: 0 !important; width: 100%; }
}

.confirm-panel { width: min(26em, 100%); }
.confirm-panel .modal-head { border-bottom: none; padding-bottom: 4px; }
.confirm-panel .modal-body p { margin: 0; color: var(--text-secondary); line-height: 1.55; }
.confirm-panel .modal-body p[hidden] { display: none; }
.confirm-panel .modal-foot { border-top: none; padding-top: 4px; }

.modal-total {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 12px 0 10px; border-bottom: 1px solid var(--grid); margin-bottom: 4px;
}
.modal-total b { font-size: var(--font-2xl); font-weight: var(--fw-chiffre); letter-spacing: -.02em; }
.modal-total:has(+ .obj-suivi) { border-bottom: none; margin-bottom: 0; padding-bottom: 8px; }
.obj-suivi { display: flex; flex-direction: column; gap: 8px; }
.obj-avancement { margin: 0; font-size: var(--font-sm); }
.obj-reponse { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.obj-reponse p { margin: 0; font-size: var(--font-md); }
.obj-chiffre { font-size: var(--font-xl); font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.obj-depart { display: flex; align-items: center; justify-content: space-between; gap: 8px;
              margin-top: 4px; font-size: var(--font-sm); }
.obj-texte { margin: 0; line-height: 1.5; font-size: var(--font-sm); color: var(--text-secondary); }
.modal-total span { font-size: var(--font-sm); color: var(--muted); }
.modal-total span { text-align: right; }
.modal-body table td { padding: 8px 6px; }
.modal-body table td:first-child { padding-left: 0; }
.modal-body table td:last-child { padding-right: 0; }
/* Un libelle et sa precision se replient dans une fenetre. La regle generale
   `td { white-space: nowrap }` vaut pour les tableaux de page, qui vivent dans
   un conteneur defilant : la colonne s'elargit, on defile, rien n'est perdu.
   Une fenetre n'a pas ce conteneur. Une note longue — « 16 EUR de marge · Mois
   calme, gros stock complements » — poussait donc le tableau hors du cadre et
   emportait la colonne des montants avec elle : « 1 18… » pour 1 189 EUR, sous
   une barre de defilement horizontale.
   Seule la premiere colonne cede : les montants gardent leur `nowrap`, sans
   quoi « 1 189,00 EUR » se casserait entre le nombre et sa devise. */
.modal-body table td.name { white-space: normal; }
.modal-body table td .petit { font-size: var(--font-sm); }
.modal-body { overflow-x: hidden; }

@media (max-width: 900px) {
  .modal { place-items: end center; padding: 0; }
  .modal-panel {
    width: 100%;
    /* `vh` sur telephone mesure la fenetre barre d'adresse repliee, pas celle
       qu'on voit. La feuille faisait donc 88 % d'une hauteur plus grande que
       l'ecran : son pied, et avec lui la fin du corps defilant, tombaient sous
       la barre du navigateur. On croyait que le defilement etait casse, alors
       que la fenetre depassait par le bas.
       `dvh` suit la fenetre reellement visible. La declaration en `vh` reste
       au-dessus pour les navigateurs qui ignorent `dvh` : meme propriete, la
       derniere comprise gagne. */
    max-height: 88vh;
    max-height: 88dvh;
    border-radius: var(--radius) var(--radius) 0 0; border-bottom: none;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    animation: modal-feuille var(--duree-fenetre) cubic-bezier(.22, .68, .28, 1);
  }
  @keyframes modal-feuille { from { transform: translate3d(0, 100%, 0) } to { transform: none } }
  .modal-foot .btn { flex: 1; justify-content: center; }
}

/* « Enregistrer et en ajouter une autre » prend sa ligne, sous les deux autres.
   Six mots ne se mettent pas au coude a coude avec « Annuler » et « Ajouter » :
   a 375 px les trois boutons faisaient 107 px chacun et le libelle se pliait en
   quatre. Le pied se replie deja, mais le repli tombait ou il tombait ; une
   ligne declaree vaut mieux qu'un repli subi.

   Cette regle vient **apres** le bloc telephone, et ce n'est pas un rangement :
   `.modal-foot .btn { flex: 1 }` y pose `flex-basis: 0`, meme specificite que
   `.modal-foot .btn-encore`. Ecrite avant, elle perdait — mesure : trois boutons
   sur une seule ligne. Meme piege que `.table-serree`, deja note dans
   `CLAUDE.md` : a specificite egale, c'est l'ordre qui tranche. */
.modal-foot .btn-encore { flex: 1 0 100%; justify-content: center; }

.lancement {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center;
  background: var(--page);
  transition: opacity .34s ease, visibility .34s;
}
.lancement.parti { opacity: 0; visibility: hidden; pointer-events: none; }
/* Le nom se colle au logo, le slogan respire dessous : `gap` uniforme les
   aurait espaces pareil, et les trois mots auraient eu l'air d'une troisieme
   information au lieu d'une precision sur la deuxieme. */
.lancement-marque { display: grid; justify-items: center; gap: 12px; }
.lancement-marque img {
  /* 23 %, le rayon du dessin — voir `.brand-mark`. A 20 px sur 72, l'arrondi
     depassait celui de la tuile et lui rognait les coins. */
  border-radius: 23%; display: block;
  animation: marque-monte .5s cubic-bezier(.16,1,.3,1) both;
}
.lancement-marque span {
  font-size: var(--font-lg); font-weight: 680; letter-spacing: -.01em; color: var(--text-primary);
  animation: marque-monte .5s cubic-bezier(.16,1,.3,1) .09s both;
}
.lancement-slogan {
  margin-top: -4px;
  font-size: var(--font-sm); font-style: normal; font-weight: 500;
  letter-spacing: .01em; color: var(--text-secondary);
  animation: marque-monte .5s cubic-bezier(.16,1,.3,1) .18s both;
}
/* Un seul mot accentue, celui que l'application promet en propre. En
   `var(--accent)` et non en violet ecrit en dur : c'est la couleur de la marque,
   elle suit le theme, et l'ecrire deux fois la laisserait diverger. Le poids
   monte d'un cran, pas plus — un mot en gras violet sur trois suffit, deux
   feraient une enseigne. */
.lancement-slogan b { font-weight: 650; color: var(--accent); }
@keyframes marque-monte {
  from { opacity: 0; transform: translateY(10px) scale(.94); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .lancement-marque img, .lancement-marque span, .lancement-slogan { animation: none; }
  .lancement { transition: opacity .01s; }
}

.file-banner {
  display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; gap: 12px; align-items: center;
  margin: 18px 30px -4px; padding: 12px 15px; border-radius: var(--radius-sm);
  font-size: var(--font-sm); color: var(--text-secondary);
  background: color-mix(in oklab, var(--warning) 12%, var(--surface-1));
  border: 1px solid color-mix(in oklab, var(--warning) 38%, transparent);
}
.file-banner b { color: var(--text-primary); }
.file-banner code {
  background: var(--surface-2); padding: 1px 5px; border-radius: 4px;
  font-size: var(--font-sm); border: 1px solid var(--border);
}
@media (max-width: 900px) { .file-banner { margin: 14px 16px -4px; } }

.data-view { margin-top: 14px; border-top: 1px solid var(--grid); padding-top: 10px; }
.data-view summary {
  cursor: pointer; font-size: var(--font-sm); color: var(--muted); font-weight: 550;
  list-style: none; display: inline-flex; align-items: center; gap: 6px; padding: 3px 0;
}
.data-view summary::-webkit-details-marker { display: none; }
.pli-journal > summary {
  display: grid; grid-template-columns: auto max-content minmax(0, 1fr);
  align-items: baseline; column-gap: 10px; width: 100%;
  min-height: 44px; font-size: var(--font-base); color: var(--text-primary);
}
.pli-journal .pli-compte { font-weight: 600; white-space: nowrap; }
.pli-journal .pli-detail { color: var(--muted); font-size: var(--font-sm); font-variant-numeric: tabular-nums; }
.pli-journal .pli-detail b { font-weight: 650; }
.pli-journal .pli-detail { text-align: right; }
.pli-journal .pli-note { display: block; font-weight: 500; font-size: var(--font-xs, 12px); color: var(--muted); }
.pli-journal .liste-principale { margin-top: 6px; }
.journal-filtre { margin: 10px 0 4px; }
.journal-filtre-champ {
  width: 100%; min-height: 40px; padding: 8px 12px; font: inherit; font-size: var(--font-sm);
  color: var(--text-primary); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.journal-filtre-champ:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.journal-recherche {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 4px 12px; margin: 8px 0 0; font-size: var(--font-sm); color: var(--muted);
}
.journal-mois { content-visibility: auto; contain-intrinsic-size: auto 320px; }
.journal-mois-tete {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 2px 12px; margin: 16px 0 2px; padding-bottom: 6px; border-bottom: 1px solid var(--grid);
  font-size: var(--font-sm); font-weight: 650; color: var(--text-primary);
}
.journal-mois-nom { margin: 0; font: inherit; }
.journal-mois-nom::first-letter { text-transform: uppercase; }
.journal-mois-chiffres { font-weight: 500; color: var(--muted); font-variant-numeric: tabular-nums; }
.journal-mois-chiffres b { font-weight: 650; }
.periode-ventes {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px 12px; margin: 24px 4px 10px;
}
.periode-ventes-titre {
  font-size: var(--font-sm); font-weight: 650; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
.periode-ventes .carte-filtres { margin: 0; }
.data-view summary::before { content: '▸'; transition: transform .15s; display: inline-block; }
.data-view[open] summary::before { transform: rotate(90deg); }
@media (hover: hover) { .data-view summary:hover { color: var(--text-primary); } }
.data-view table { margin-top: 10px; }
.data-view .table-wrap { max-height: 320px; overflow-y: auto; }

.chevron-role {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  font: inherit; font-size: 20px; font-weight: 700; line-height: 1;
  background: none; border: none; border-radius: 50%; cursor: pointer;
  color: var(--accent);
  transition: transform .18s cubic-bezier(.22, .61, .36, 1),
              background .14s ease, opacity .14s ease;
  opacity: .8;
}
.chevron-role.ouvert { transform: rotate(90deg); }
@media (hover: hover) { .chevron-role:hover {
  opacity: 1; background: color-mix(in oklab, var(--accent) 16%, transparent);
} }
@media (hover: hover) { .chevron-role.ouvert:hover { transform: rotate(90deg) scale(1.08); } }
@media (hover: hover) { .chevron-role:not(.ouvert):hover { transform: scale(1.08); } }
.chevron-role:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.contrib-liste {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--grid);
}
.contrib-jeton {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 6px 5px 11px; border-radius: var(--radius-pill);
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: var(--font-sm); white-space: nowrap;
}
.contrib-jeton .muted { font-variant-numeric: tabular-nums; }

details.pli-reglages { margin-top: 2px; }
details.pli-reglages > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--border-strong, var(--grid)); border-radius: var(--radius);
  background: var(--surface-2);
  font-size: var(--font-sm); font-weight: 550; color: var(--text-primary);
}
details.pli-reglages > summary::-webkit-details-marker { display: none; }
@media (hover: hover) { details.pli-reglages > summary:hover { border-color: var(--accent); } }
.pli-valeurs { flex: 1 1 auto; min-width: 0; }
.pli-action {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  color: var(--accent);
}
.pli-action::after { content: '▾'; display: inline-block; transition: transform .15s; }
/* `> summary` et non un descendant : le depliant des taux vit A L'INTERIEUR de
   la carte des hypotheses, qui porte la meme classe. Sans cette restriction, la
   carte ouverte retournait AUSSI le chevron de son enfant ferme -- on lisait
   « Ouvrir ^ » sur un pli qui ne montrait rien, une fleche qui annonçait
   l'inverse de ce que le clic allait faire.

   La regle vaut pour tout depliant imbrique a venir : le chevron appartient au
   sommaire de son propre depliant, jamais a celui d'un ancetre. */
details.pli-reglages[open] > summary .pli-action::after { transform: rotate(180deg); }

.acct-head { flex-wrap: wrap; row-gap: 6px; }
.acct-nom {
  flex: 1 1 auto; min-width: 9em; width: auto;
  font: inherit; font-size: var(--font-base); font-weight: 680; color: var(--text-primary);
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-xs);
  padding: 3px 6px; margin: -3px -6px; text-align: left;
}
@media (hover: hover) { .acct-nom:hover { border-color: var(--border); background: var(--surface-2); } }
.acct-nom:focus { border-color: var(--accent); background: var(--surface-2); outline: none; }

select.annee {
  width: auto; padding: 5px 8px; font-size: var(--font-sm); font-weight: 550;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); color: var(--text-primary);
}

.mois-lien {
  font: inherit; font-weight: 680; color: var(--text-primary);
  background: none; border: none; padding: 0; cursor: pointer;
  text-align: left; text-decoration: underline; text-decoration-style: dotted;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in oklab, currentColor 45%, transparent);
}
@media (hover: hover) { .mois-lien:hover { text-decoration-color: currentColor; } }

.dep-total {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding-bottom: 12px; margin-bottom: 4px; border-bottom: 1px solid var(--grid);
}
.dep-libelle { flex-basis: 100%; font-size: var(--font-xs); font-weight: 650; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.dep-somme { font-size: var(--font-2xl); font-weight: 720; letter-spacing: -.02em; }
.dep-ecart { font-size: var(--font-md); font-weight: 600; }
.dep-grille {
  display: grid; gap: 10px 14px;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
}
.dep-grille input { text-align: right; }
.champ-somme { position: relative; display: block; }
.champ-somme > input { padding-left: 30px; }
.somme-plus {
  position: absolute; left: 5px; top: 50%; transform: translateY(-50%);
  width: 20px; height: 20px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font: inherit; font-size: var(--font-base); line-height: 1; font-weight: 700;
  color: var(--text-secondary); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--radius-xs); cursor: pointer;
}
@media (hover: hover) { .somme-plus:hover { color: var(--accent); border-color: var(--accent); } }
.somme-plus:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

