/**
 * TNToast.css — Moteur de toasts de la plateforme TelaNode (remplace PNotify).
 *
 * Composant PARTAGÉ (/assets/css/TNToast.css) chargé par TOUS les shells, en
 * binôme avec /assets/js/TNToast.js (qui surcharge tn.notify). Remplace PNotify.
 *
 * Tokens design system (`/assets/css/telanode-ds.css`) : --tn-bg-*, --tn-text-*,
 * --tn-border, --tn-shadow-*, --tn-success/danger/warning/info, --tn-radius.
 * Repli en dur sur chaque var() : le CSS reste lisible même si le DS n'est pas chargé.
 *
 * Historique :
 *   20260810 Claude+Cyrille Création (shell club) puis généralisation plateforme
 */

/* ============================== CONTENEUR ============================== */

.tn-toast-wrap {
	position: fixed;
	z-index: 1080;                 /* > modal Bootstrap (1055), < IconPicker (1070+) : cf. note z-index ci-dessous */
	display: flex;
	flex-direction: column;
	gap: 10px;
	pointer-events: none;          /* seules les toasts captent la souris */
	/* ZONE d'affichage de largeur PROPRE (2/3 de l'écran), pas « la largeur des
	   toasts » : les toasts s'y dimensionnent librement (max-content, bornées à 100%)
	   et s'alignent sur le coin via align-items.
	   ⚠ NE PAS remettre `width: max-content` ici : combiné au `max-width: 100%` de
	   la toast, cela crée une dépendance CIRCULAIRE (parent↔enfant) que le navigateur
	   résout en retombant sur la `min-width` de la toast — conteneur figé à 280px et
	   contenu coupé. */
	width: min(66vw, calc(100vw - 40px));
	max-height: calc(100vh - 92px);
	overflow: hidden;
	/* `padding` + `margin` négative de même valeur : la zone de clipping s'élargit de
	   24px sans déplacer les toasts d'un pixel — sinon `overflow:hidden` rognerait
	   l'animation d'entrée/sortie, qui décale la toast de 24px vers l'extérieur. */
	padding: 24px;
	margin: -24px;
	box-sizing: content-box;   /* la largeur reste bien celle de la ZONE, hors padding */
}

/* Les 4 coins (préférence utilisateur, défaut bas-droite).
   `column-reverse` fait apparaître la plus récente PRÈS du coin. */
.tn-toast-wrap.pos-br { right: 20px; bottom: 20px; flex-direction: column-reverse; align-items: flex-end; }
.tn-toast-wrap.pos-bl { left: 20px;  bottom: 20px; flex-direction: column-reverse; align-items: flex-start; }
.tn-toast-wrap.pos-tr { right: 20px; top: 72px;    flex-direction: column;         align-items: flex-end; }
.tn-toast-wrap.pos-tl { left: 20px;  top: 72px;    flex-direction: column;         align-items: flex-start; }
/* top: 72px → sous la role-bar fixe du shell (56px + marge). */

/* ============================== TOAST ============================== */

.tn-toast {
	pointer-events: auto;
	position: relative;
	display: flex;
	/* `stretch` (et NON flex-start) : sans lui, la colonne principale garde sa
	   hauteur de contenu, dépasse la toast et se fait simplement couper par
	   overflow:hidden — aucun ascenseur n'apparaît jamais. */
	align-items: stretch;
	gap: 10px;
	/* Largeur ADAPTATIVE au contenu : une notification courte reste compacte, un long
	   rapport s'élargit — jusqu'au plafond porté par le conteneur (2/3 de l'écran). */
	width: max-content;
	min-width: 280px;
	max-width: 100%;
	/* Hauteur bornée aux 2/3 de l'écran : au-delà, c'est le TEXTE qui défile
	   (cf. .tn-toast-text), jamais la toast qui sort de l'écran. */
	max-height: 66vh;
	overflow: hidden;
	padding: 12px 12px 12px 14px;
	background: var(--tn-bg-primary, #fff);
	color: var(--tn-text-1, #1f2937);
	border: 1px solid var(--tn-border, #e5e7eb);
	border-left: 4px solid var(--tn-toast-accent, var(--tn-info, #06b6d4));
	border-radius: var(--tn-radius, 6px);
	box-shadow: var(--tn-shadow-lg, 0 8px 24px rgba(0,0,0,.12));
	overflow: hidden;
	opacity: 0;
	transition: opacity .22s ease, transform .22s ease;
	will-change: transform, opacity;
}

/* Entrée/sortie : glissement depuis le bord HORIZONTAL du coin (desktop). */
.tn-toast-wrap.pos-br .tn-toast,
.tn-toast-wrap.pos-tr .tn-toast { transform: translateX(24px); }
.tn-toast-wrap.pos-bl .tn-toast,
.tn-toast-wrap.pos-tl .tn-toast { transform: translateX(-24px); }
.tn-toast.is-in  { opacity: 1; transform: translateX(0); }
.tn-toast.is-out { opacity: 0; }
.tn-toast-wrap.pos-br .tn-toast.is-out,
.tn-toast-wrap.pos-tr .tn-toast.is-out { transform: translateX(24px); }
.tn-toast-wrap.pos-bl .tn-toast.is-out,
.tn-toast-wrap.pos-tl .tn-toast.is-out { transform: translateX(-24px); }

.tn-toast-icon {
	flex: 0 0 auto;
	align-self: flex-start;   /* reste en haut malgré le stretch du conteneur */
	width: 22px; height: 22px;
	display: flex; align-items: center; justify-content: center;
	font-size: 15px;
	color: var(--tn-toast-accent, var(--tn-info, #06b6d4));
}

/* Colonne principale : en-tête FIXE (titre + actions) puis texte DÉFILANT.
   `min-height: 0` est indispensable à chaque niveau — sans lui un enfant flex
   refuse de rétrécir sous sa hauteur de contenu, et le débordement ressort du
   conteneur (coupé par overflow:hidden) au lieu de produire un ascenseur. */
.tn-toast-main {
	flex: 1 1 auto;
	min-width: 0;
	min-height: 0;
	display: flex;
	flex-direction: column;
}

/* En-tête : titre à gauche, boutons collés à droite. Ne défile jamais. */
.tn-toast-head {
	flex: 0 0 auto;
	display: flex;
	align-items: flex-start;
	gap: 8px;
}
/* Le titre vit dans l'en-tête FIXE : il est borné en hauteur par sécurité (le JS
   rebascule normalement un titre long vers le corps défilant — cf. splitTitleBody —
   mais cette ceinture garantit qu'aucun titre ne peut faire déborder la toast). */
.tn-toast-title {
	flex: 1 1 auto;
	min-width: 0;
	max-height: 4.5em;
	overflow-y: auto;
	scrollbar-width: thin;
}

.tn-toast-title {
	font-weight: 600;
	font-size: .9rem;
	line-height: 1.3;
	margin: 0 0 2px;
	word-break: break-word;
	white-space: pre-wrap;
}

/* Le TEXTE est la seule zone qui défile (l'en-tête reste en place). */
.tn-toast-text {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;   /* le défilement ne se propage pas à la page */
	scrollbar-width: thin;
	font-size: .84rem;
	line-height: 1.4;
	color: var(--tn-text-2, #4b5563);
	word-break: break-word;
	/* Conserve les retours à la ligne du message d'origine (rapports multi-lignes,
	   traces d'erreur…) tout en laissant le texte se replier sur la largeur.
	   `pre-wrap` ≠ `pre` : pas de défilement horizontal forcé. Le HTML éventuel
	   (<strong>, <br>…) reste interprété. */
	white-space: pre-wrap;
}
.tn-toast-text:empty { display: none; }
.tn-toast-title:empty { display: none; }

/* Ascenseur discret du texte (WebKit ; Firefox utilise scrollbar-width). */
.tn-toast-text::-webkit-scrollbar { width: 8px; }
.tn-toast-text::-webkit-scrollbar-thumb {
	background: var(--tn-border, #e5e7eb);
	border-radius: 4px;
}
.tn-toast-text::-webkit-scrollbar-thumb:hover { background: var(--tn-text-4, #9ca3af); }
/* Le texte peut contenir du HTML (comportement historique titleTrusted/textTrusted). */
.tn-toast-text p:last-child { margin-bottom: 0; }

/* ---- Actions (desktop uniquement) ---- */
/* Dans l'en-tête, poussées à droite. Plus besoin de `position: sticky` : l'en-tête
   ne défile pas, donc les boutons sont toujours visibles, et ils ne débordent plus
   de la toast puisqu'ils sont contenus dans la colonne principale. */
.tn-toast-actions {
	flex: 0 0 auto;
	display: flex;
	gap: 2px;
	margin-left: auto;
}

.tn-toast-btn {
	appearance: none;
	background: transparent;
	border: 0;
	padding: 3px 5px;
	line-height: 1;
	font-size: 12px;
	color: var(--tn-text-4, #9ca3af);
	cursor: pointer;
	border-radius: var(--tn-radius-sm, 4px);
	transition: color .15s ease, background .15s ease;
}
.tn-toast-btn:hover  { color: var(--tn-text-1, #1f2937); background: var(--tn-bg-tertiary, #f3f4f6); }
.tn-toast-btn:focus-visible { outline: 2px solid var(--tn-info, #06b6d4); outline-offset: 1px; }
.tn-toast-btn.is-pinned { color: var(--tn-warning, #f59e0b); }

/* ---- Barre de progression d'auto-fermeture ---- */
.tn-toast-progress {
	position: absolute;
	left: 0; bottom: 0;
	height: 2px;
	width: 100%;
	transform-origin: left center;
	background: var(--tn-toast-accent, var(--tn-info, #06b6d4));
	opacity: .5;
}
.tn-toast.is-pinned .tn-toast-progress { display: none; }

/* ---- Accents par type ---- */
.tn-toast.t-success { --tn-toast-accent: var(--tn-success, #22c55e); }
.tn-toast.t-error   { --tn-toast-accent: var(--tn-danger,  #ef4444); }
.tn-toast.t-warning { --tn-toast-accent: var(--tn-warning, #f59e0b); }
.tn-toast.t-info    { --tn-toast-accent: var(--tn-info,    #06b6d4); }
.tn-toast.t-notice  { --tn-toast-accent: var(--tn-text-3,  #6b7280); }

/* ============================== MOBILE ============================== */
/* Une seule toast à la fois, PLEINE LARGEUR (on ignore le réglage gauche/droite),
   ancrée en HAUT ou en BAS selon la seule composante verticale de la préférence,
   au-dessus des safe-areas. */
@media (max-width: 768px) {
	/* Mobile : bande PLEINE LARGEUR. On IGNORE volontairement le réglage gauche/droite
	   (le coin n'a pas de sens sur un petit écran) — seule compte la verticale HAUT/BAS.
	   `width` vient de left+right (mêmes valeurs quel que soit le coin choisi). */
	.tn-toast-wrap,
	.tn-toast-wrap.pos-br,
	.tn-toast-wrap.pos-bl,
	.tn-toast-wrap.pos-tr,
	.tn-toast-wrap.pos-tl {
		left: 12px;
		right: 12px;
		max-width: none;
		width: auto;
		/* Animation verticale ici : on n'élargit la zone de clipping que sur l'axe Y
		   (un padding horizontal décalerait le calage sur left/right). */
		padding: 24px 0;
		margin: -24px 0;
		align-items: stretch;
	}

	/* BAS (défaut · bl/br) : ancré en bas, la plus récente près du bord. */
	.tn-toast-wrap {
		bottom: calc(12px + env(safe-area-inset-bottom, 0px));
		top: auto;
		flex-direction: column-reverse;
	}
	/* HAUT (tl/tr) : ancré en haut, sous la barre du shell. Plus spécifique → l'emporte. */
	.tn-toast-wrap.pos-tl,
	.tn-toast-wrap.pos-tr {
		top: calc(64px + env(safe-area-inset-top, 0px));
		bottom: auto;
		flex-direction: column;
	}

	.tn-toast {
		/* Mobile : pleine largeur (pas de 2/3) et hauteur max plus généreuse —
		   l'écran est petit, autant exploiter la place ; le corps défile au-delà. */
		width: auto;
		min-width: 0;
		max-width: none;
		max-height: 80vh;
		padding: 14px;
		transform: translateY(24px);      /* défaut : entrée par le BAS */
		touch-action: pan-y;              /* le swipe est géré en JS (pointer events) */
	}
	/* Une toast longue devient scrollable : le swipe de fermeture ne doit pas
	   entrer en conflit avec le défilement du texte (géré en JS via la cible). */
	.tn-toast-wrap { max-height: calc(100vh - 24px); }
	.tn-toast.is-in  { transform: translateY(0); }
	.tn-toast.is-out { transform: translateY(24px); }

	/* Position HAUT → l'entrée/sortie se fait par le HAUT (translation inverse). */
	.tn-toast-wrap.pos-tl .tn-toast,
	.tn-toast-wrap.pos-tr .tn-toast { transform: translateY(-24px); }
	.tn-toast-wrap.pos-tl .tn-toast.is-in,
	.tn-toast-wrap.pos-tr .tn-toast.is-in { transform: translateY(0); }
	.tn-toast-wrap.pos-tl .tn-toast.is-out,
	.tn-toast-wrap.pos-tr .tn-toast.is-out { transform: translateY(-24px); }

	/* Épingle et × masqués : sur mobile on ferme au swipe ou au tap. */
	.tn-toast-actions { display: none; }

	/* Pendant le glissement, le JS pilote transform/opacity → pas de transition. */
	.tn-toast.is-dragging { transition: none; }
}

/* =================== SÉLECTEUR DE POSITION (user-menu) =================== */
/* Mini-plan d'écran 2×2 : chaque bouton figure un coin. */
.tn-toastpos-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 4px;
	width: 92px;
}
.tn-toastpos-btn {
	position: relative;
	height: 26px;
	padding: 0;
	background: var(--tn-bg-secondary, #f9fafb);
	border: 1px solid var(--tn-border, #e5e7eb);
	border-radius: var(--tn-radius-sm, 4px);
	cursor: pointer;
	transition: border-color .15s ease, background .15s ease;
}
.tn-toastpos-btn::after {           /* la « toast » miniature, positionnée dans le coin */
	content: '';
	position: absolute;
	width: 14px; height: 5px;
	border-radius: 2px;
	background: var(--tn-text-4, #9ca3af);
}
.tn-toastpos-btn[data-pos="tl"]::after { top: 4px;    left: 4px; }
.tn-toastpos-btn[data-pos="tr"]::after { top: 4px;    right: 4px; }
.tn-toastpos-btn[data-pos="bl"]::after { bottom: 4px; left: 4px; }
.tn-toastpos-btn[data-pos="br"]::after { bottom: 4px; right: 4px; }
.tn-toastpos-btn:hover { border-color: var(--tn-text-3, #6b7280); }
.tn-toastpos-btn.is-active {
	background: var(--tn-bg-selected, #dbeafe);
	border-color: var(--tn-info, #06b6d4);
}
.tn-toastpos-btn.is-active::after { background: var(--tn-info, #06b6d4); }
.tn-toastpos-btn:focus-visible { outline: 2px solid var(--tn-info, #06b6d4); outline-offset: 1px; }

/* ============================== THÈME SOMBRE ============================== */
/* Le shell club force aujourd'hui data-theme="light" ; ces règles préparent la
   bascule sans rien changer au rendu actuel. */
:root[data-theme="dark"] .tn-toast {
	background: var(--tn-bg-primary, #1f2937);
	color: var(--tn-text-1, #f3f4f6);
	border-color: var(--tn-border, #374151);
}
@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) .tn-toast {
		background: var(--tn-bg-primary, #1f2937);
		color: var(--tn-text-1, #f3f4f6);
		border-color: var(--tn-border, #374151);
	}
}

/* ============================== ACCESSIBILITÉ ============================== */
@media (prefers-reduced-motion: reduce) {
	.tn-toast { transition: opacity .01s linear; transform: none !important; }
	.tn-toast-progress { transition: none !important; }
}
