/* Templiz Site Builder — shared structure. Visual identity (color, type,
   section layout details) lives entirely in each template-*.css file via
   the --tsb-* custom properties set inline by the shortcode. */

.tsb-site {
	background: var(--tsb-paper);
	color: var(--tsb-ink);
	font-family: var(--tsb-font-body);
	line-height: 1.6;
	overflow-x: hidden;
}

.tsb-site * { box-sizing: border-box; }

.tsb-site img { max-width: 100%; display: block; }

/* 41e vague, retour Chloé — investigation "couleurs personnalisables" /
   dissociation du texte : cette règle de remise à zéro (couleur de lien =
   couleur du texte environnant) a une spécificité CSS de (0,1,1) — une
   classe + un sélecteur de balise — supérieure à celle de nombreuses
   règles "avancées" à une seule classe censées la remplacer (ex.
   .tsb-nav-brand, .tsb-nav-link, .tsb-nav-social-link, toutes à
   spécificité (0,1,0)). Résultat : sur ordinateur (hors media query
   mobile), ces liens ignoraient silencieusement "Texte de l'en-tête" /
   "Icônes réseaux sociaux" et retombaient sur "Texte" (--tsb-ink) — un vrai
   bug de cascade CSS, distinct du bug d'enregistrement corrigé en 40e
   vague, et confirmé en direct sur clairounails.templiz.com. Passer par
   :where(...) ramène la spécificité de cette règle à zéro : elle continue
   de s'appliquer par défaut (elle reste la dernière déclarée à spécificité
   égale face aux styles par défaut du thème) mais ne peut plus jamais
   l'emporter sur une règle plus spécifique dédiée à un élément précis,
   aujourd'hui ou dans une future vague. */
:where(.tsb-site a) { color: inherit; }

/* v1.9.1 — same tap-highlight cleanup as `.tsb-nav-toggle` below, applied
   site-wide: any link/button could otherwise flash the browser's default
   blue tint on tap. Native focus outlines are left alone here (removed
   only on the icon-only nav toggle, which gets its own visible
   :focus-visible ring) so keyboard accessibility isn't affected. */
.tsb-site a, .tsb-site button { -webkit-tap-highlight-color: transparent; }

.tsb-site .screen-reader-text {
	position: absolute;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden;
	clip: rect(0,0,0,0);
	white-space: nowrap;
	border: 0;
}

.tsb-section-title {
	font-family: var(--tsb-font-display);
	font-size: 30px;
	font-weight: 500;
	margin: 0 0 32px;
	/* item #102 — Chloé: "Mes couleurs" custom title color. Unset on every
	   built-in palette and on Personnalisé left blank, so this falls back
	   to the inherited --tsb-ink (.tsb-site's own color), unchanged. */
	color: var(--tsb-title, var(--tsb-ink));
}

/* Nav — site-wide, every page (v1.4.0: real separate pages) */
.tsb-nav {
	position: sticky;
	top: 0;
	z-index: 40;
	/* 31e vague, retour Chloé — "Mes couleurs" : fond et texte de l'en-tête
	   désormais réglables indépendamment (voir class-tsb-shortcode.php) ;
	   même garde-fou "non réglé = comportement inchangé" que cta_bg/
	   cta_text/title. */
	background: var(--tsb-header-bg, var(--tsb-paper));
	border-bottom: 1px solid transparent;
	transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.tsb-nav.is-scrolled {
	border-bottom-color: var(--tsb-sand);
	box-shadow: 0 2px 14px rgba(0,0,0,0.05);
}
.tsb-nav-inner {
	/* v1.9.0 — Chloé: no max-width/auto-margin here (unlike every other
	   section, which is full-width with just 6vw padding) — on wide
	   screens this was inset ~50px+ further right than the hero/content
	   below it, reading as a stray blank gap before the site name. */
	padding: 16px 6vw;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 24px;
}
.tsb-nav-brand {
	display: flex;
	align-items: center;
	gap: 10px;
	text-decoration: none;
	font-family: var(--tsb-font-display);
	font-size: 18px;
	font-weight: 500;
	flex-shrink: 0;
	/* 31e vague, retour Chloé — "Mes couleurs" : texte de l'en-tête (nom du
	   site + liens du menu). */
	color: var(--tsb-header-text, var(--tsb-ink));
}
.tsb-nav-menu { display: flex; align-items: center; gap: 28px; }
.tsb-nav-link {
	text-decoration: none;
	font-size: 14px;
	opacity: 0.75;
	white-space: nowrap;
	color: var(--tsb-header-text, var(--tsb-ink));
}
.tsb-nav-link:hover { opacity: 1; }
.tsb-nav-link.is-active { opacity: 1; border-bottom: 1px solid var(--tsb-accent); padding-bottom: 3px; }
.tsb-nav-cta { padding: 10px 22px; font-size: 13px; }
/* 36e vague, retour Chloé — item 6 : couleur dédiée du bouton "Réserver" de
   l'en-tête, indépendante du CTA générique (cta_bg/cta_text) partagé par
   tous les autres boutons pleins du site — non réglée, retombe sur
   cta_bg/cta_text (voir .tsb-btn-primary ci-dessous), donc aucun changement
   pour un site qui ne touche pas ce nouveau champ.
   37e vague, retour Chloé — VRAI bug trouvé ici : sur les templates
   Signature/Loft/Studio/Nature, chacun définit son propre
   ".tsb-site[data-tsb-template=X] .tsb-btn-primary" (3 sélecteurs) pour
   styler SES boutons pleins — plus spécifique que la règle ci-dessous (2
   sélecteurs) ET chargé après base.css, donc il gagnait systématiquement et
   repeignait le bouton "Réserver" avec le CTA générique (--tsb-cta-bg),
   rendant header_cta_bg/header_cta_text muets sur ces 4 templates. Cette
   règle-ci reste donc le cas de repli pour Atelier (qui ne redéfinit pas
   .tsb-btn-primary) — un correctif dédié à specificité supérieure a été
   ajouté dans chacun des 4 fichiers template-*.css pour les autres, en
   conservant la couleur par défaut propre à chaque template quand
   header_cta_bg n'est pas réglé (voir leur propre commentaire). */
.tsb-nav-cta.tsb-btn-primary { background: var(--tsb-header-cta-bg, var(--tsb-cta-bg, var(--tsb-ink))); color: var(--tsb-header-cta-text, var(--tsb-cta-text, var(--tsb-paper))); }
.tsb-nav-social { display: inline-flex; align-items: center; gap: 14px; }
/* 34e vague — couleur dédiée, indépendante de header_text (voir
   TSB_Templates::default_custom_colors()). */
.tsb-nav-social-link { display: inline-flex; align-items: center; color: var(--tsb-header-social, var(--tsb-header-text, var(--tsb-ink))); opacity: 0.75; }
.tsb-nav-social-link:hover { opacity: 1; }
.tsb-nav-social-icon { display: block; }
.tsb-nav-toggle {
	display: none;
	flex-direction: column;
	justify-content: center;
	gap: 5px;
	width: 32px;
	height: 32px;
	background: none;
	border: none;
	cursor: pointer;
	padding: 0;
	/* v1.9.1 — Chloé: "quand on clique sur le menu l'icone devient bleu".
	   That's the browser's own default tap/focus highlight on a bare
	   <button> (WebKit's tap-highlight tint, Chrome's blue focus ring) —
	   nothing in our CSS was drawing it. Turned off, with a neutral
	   `:focus-visible` ring kept for keyboard users so accessibility
	   doesn't regress. */
	-webkit-tap-highlight-color: transparent;
	outline: none;
}
.tsb-nav-toggle:focus-visible { outline: 2px solid var(--tsb-ink); outline-offset: 3px; }
.tsb-nav-toggle span { display: block; width: 100%; height: 1.5px; background: var(--tsb-ink); }
/* v1.9.2 — Chloé: "pour l'icône du menu sur mobile la sélection ne doit pas
   être bleu". Different cause than the v1.9.1 fix above: Astra's theme CSS
   targets `.menu-toggle`/`button` generically (`.menu-toggle, button, ... {
   background: var(--ast-global-color-0) }` and a `:hover`/`:focus` variant
   using `--ast-global-color-1`), both of which resolve to Astra's blue and
   both of which beat our plain `.tsb-nav-toggle` selector on specificity
   once the button keeps focus after being tapped. Forced back to
   transparent in every state so Astra can never repaint it. */
.tsb-nav-toggle,
.tsb-nav-toggle:hover,
.tsb-nav-toggle:focus,
.tsb-nav-toggle:active,
.tsb-nav-toggle[aria-expanded="true"] {
	background: none !important;
	background-color: transparent !important;
}

/* item #90 — "centered" header layout: site name on its own row, menu
   centered below it. Desktop only (min-width matches the existing mobile
   breakpoint below) — on mobile every layout already collapses to the
   same hamburger dropdown, so nothing here needs to touch that. */
@media (min-width: 861px) {
	.tsb-nav-layout-centered .tsb-nav-inner {
		flex-direction: column;
		justify-content: center;
		gap: 14px;
	}
	.tsb-nav-layout-centered .tsb-nav-brand { font-size: 20px; }
}

@media (max-width: 860px) {
	.tsb-nav-toggle { display: flex; }
	.tsb-nav-menu {
		position: absolute;
		top: 100%;
		left: 0;
		right: 0;
		flex-direction: column;
		align-items: flex-start;
		gap: 4px;
		/* 31e vague, retour Chloé — "Mes couleurs" : "menu arrière-plan et
		   texte" réglable indépendamment de l'en-tête (barre du haut) —
		   retombe sur header_bg/header_text si non réglé, lui-même retombant
		   sur paper/ink si lui non plus n'est pas réglé. */
		background: var(--tsb-mobile-menu-bg, var(--tsb-header-bg, var(--tsb-paper)));
		border-bottom: 1px solid var(--tsb-sand);
		padding: 8px 6vw 20px;
		display: none;
	}
	.tsb-nav-menu.is-open { display: flex; }
	.tsb-nav-menu .tsb-nav-link { color: var(--tsb-mobile-menu-text, var(--tsb-header-text, var(--tsb-ink))); }
	.tsb-nav-link { padding: 10px 0; width: 100%; }
	.tsb-nav-cta { margin-top: 8px; }
	.tsb-nav-social { margin-top: 14px; }
	/**
	 * 61e vague, retour Chloé — item 6 : "Rends toutes les pages des sites des
	 * prestataires responsives" — .tsb-nav-brand a flex-shrink:0 (voir
	 * règle de base ci-dessus) et aucune largeur maximale : avec un nom
	 * d'établissement long, il gardait sa largeur naturelle sur une seule
	 * ligne quelle que soit la place restante dans .tsb-nav-inner, ce qui
	 * pouvait repousser le bouton hamburger hors de l'écran sur un téléphone
	 * étroit — masqué par le overflow-x:hidden de .tsb-site, donc invisible
	 * et incliquable, plus moyen d'ouvrir le menu. Plafonné ici à la largeur
	 * disponible moins le bouton (32px) et l'espacement (.tsb-nav-inner a
	 * gap:24px), avec troncature en "…" plutôt qu'un débordement silencieux.
	 */
	.tsb-nav-brand {
		min-width: 0;
		max-width: calc(100% - 56px);
	}
	.tsb-nav-brand span {
		display: block;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}
}

/* Page banner — compact header on every page except Accueil */
.tsb-page-banner {
	padding: 48px 6vw 36px;
	border-bottom: 1px solid var(--tsb-sand);
}
.tsb-page-banner-kicker {
	font-size: 13px;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	/* v1.9.6 — item #85: was the raw accent color, unreadable for several
	   palettes (ex. "Jaune") on the paper background — see --tsb-accent-text
	   in class-tsb-shortcode.php, a contrast-checked version of the same
	   color. */
	color: var(--tsb-accent-text, var(--tsb-accent));
	margin: 0 0 8px;
}
.tsb-page-banner-title {
	font-family: var(--tsb-font-display);
	font-size: clamp(28px, 4vw, 42px);
	margin: 0;
	/* item #102 — same custom title color as .tsb-section-title above;
	   same unset-falls-back-to-ink guarantee. Studio overrides this to a
	   fixed paper-on-dark-banner color regardless (see template-studio.css),
	   so this never fights that contrast-safety rule. */
	color: var(--tsb-title, var(--tsb-ink));
}

/* Closing CTA banner (Accueil only) — v1.6.0: rounded card, text left /
   actions right, closer to xo-hair-melun's "RÉSERVATION" block. */
.tsb-cta-banner { padding: 32px 6vw 70px; }
/* 31e vague, retour Chloé — "Mes couleurs" : fond du bloc CTA de clôture
   (Accueil) réglable indépendamment ; non réglé = --tsb-sand, inchangé. */
.tsb-cta-banner-inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 24px;
	padding: 40px 44px;
	background: var(--tsb-cta-banner-bg, var(--tsb-sand));
	border-radius: 20px;
}
.tsb-cta-kicker { font-size: 12px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--tsb-accent-text, var(--tsb-accent)); margin: 0 0 10px; }
/* 110e vague (suite, 3), retour Chloé — "il manque l'option pour la
   couleur du texte du bloc réservation" : ni l'un ni l'autre n'avait de
   couleur réglable avant (le titre n'avait même aucune couleur fixée du
   tout) ; non réglé = var(--tsb-cta-banner-text, ...) retombe sur
   "inherit", rendu strictement identique à avant. */
.tsb-cta-title { font-family: var(--tsb-font-display); font-size: clamp(24px, 3vw, 32px); margin: 0 0 10px; max-width: 20ch; color: var(--tsb-cta-banner-text, inherit); }
.tsb-cta-text { max-width: 40ch; margin: 0; opacity: 0.85; color: var(--tsb-cta-banner-text, inherit); }
.tsb-cta-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* 126e vague, retour Chloé — "ajoute une option pour la personnalisation de
   couleur pour le deuxième CTA (instagram) -> fond et texte" : ce pill
   "@handle" n'avait jusqu'ici aucune couleur réglable (fond toujours
   transparent, texte toujours hérité via :where(.tsb-site a){color:inherit}
   plus haut dans ce fichier — pas de bug de lien bleu ici, contrairement au
   footer en 46e vague, juste aucune option). Non réglé = fond transparent et
   texte "inherit", rendu strictement identique à avant. Bordure
   volontairement laissée telle quelle (var(--tsb-ink), héritée de .tsb-btn) :
   seuls fond et texte ont été demandés. */
.tsb-cta-instagram {
	opacity: 0.85;
	background: var(--tsb-cta-banner-instagram-bg, transparent);
	color: var(--tsb-cta-banner-instagram-text, inherit);
}
/* 110e vague (suite, 2), retour Chloé — "il manque les options pour la
   couleur du texte et la couleur du CTA" (bandeau de réservation) : le
   bouton "Prendre rendez-vous" de cette carte n'avait pas de champ dédié,
   contrairement aux autres CTA du site (en-tête, héros, Urgence) — il
   suivait seulement le réglage général "Fond/Texte des boutons (CTA)".
   Non réglé = suit encore ce même réglage général (var(--tsb-cta-bg,
   var(--tsb-ink))), donc aucun changement pour qui n'y touche pas. */
.tsb-cta-actions .tsb-btn-primary {
	background: var(--tsb-cta-banner-cta-bg, var(--tsb-cta-bg, var(--tsb-ink)));
	color: var(--tsb-cta-banner-cta-text, var(--tsb-cta-text, var(--tsb-paper)));
}

/* Urgence banner (Accueil only) */
/* 33e vague, retour Chloé — "mets un espace entre le bloc les plus
   populaires et le bloc urgence" : ce bloc suit directement .tsb-services
   dans le DOM (voir TSB_Shortcode::render()) et n'avait aucun padding-top,
   collé au padding-bottom de .tsb-services — ajouté ici plutôt que sur
   .tsb-services pour ne pas modifier l'espacement de "Les plus populaires"
   avec les AUTRES blocs qui peuvent le suivre quand "urgence" est désactivé. */
.tsb-urgency { padding: 40px 6vw 60px; }
.tsb-urgency-card {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 24px;
	padding: 32px 36px;
	/* 110e vague (suite), retour Chloé — nouveau champ "Mes couleurs" dédié
	   (urgency_bg) ; vide par défaut, donc inchangé pour toute prestataire
	   qui n'y touche pas. */
	background: var(--tsb-urgency-bg, var(--tsb-sand));
	border-radius: 20px;
}
.tsb-urgency-kicker { font-size: 12px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--tsb-accent-text, var(--tsb-accent)); margin: 0 0 8px; }
/* 127e vague, retour Chloé — "couleur titre, couleur texte [...] pour la
   carte prestation en urgence" : ni le titre ni le texte n'avaient de
   couleur fixée du tout avant (contrairement au kicker ci-dessus, déjà sur
   l'accent) — non réglé = inherit, rendu strictement identique à avant. */
.tsb-urgency-title { font-family: var(--tsb-font-display); font-size: 20px; margin: 0 0 8px; color: var(--tsb-urgency-title, inherit); }
.tsb-urgency-text { font-size: 14px; opacity: 0.8; margin: 0; max-width: 56ch; color: var(--tsb-urgency-text, inherit); }
/* v1.9.2 — Chloé: "le bouton ne doit pas être bleu mais comme l'une des
   couleurs du thème". .tsb-btn only sets layout (padding/border/radius);
   this CTA never had .tsb-btn-primary or any other color modifier, so it
   fell back to the browser/host theme's default link blue. Uses the site's
   own accent color instead.
   127e vague — "couleur fond CTA, couleur texte du CTA" : fond/texte
   figés en dur sur l'accent du thème jusqu'ici, aucun champ dédié ; non
   réglé = retombe sur ce même accent, inchangé. border-color suit
   désormais urgency_cta_bg plutôt que l'accent en dur, pour rester un
   bouton plein cohérent (fond et bordure toujours identiques) même avec
   un fond personnalisé. */
.tsb-urgency-cta {
	flex-shrink: 0;
	background: var(--tsb-urgency-cta-bg, var(--tsb-accent));
	color: var(--tsb-urgency-cta-text, var(--tsb-on-accent, var(--tsb-paper)));
	border-color: var(--tsb-urgency-cta-bg, var(--tsb-accent));
}
.tsb-urgency-cta:hover { opacity: 0.9; }

/* v1.9.0 — Chloé item #4: "Me contacter" popover (message/email/WhatsApp)
   — see section-urgency.php. Positioned relative to the CTA button itself
   so the menu sits right under it regardless of where the banner appears
   (Accueil preview vs. its pinned fallback spot). */
.tsb-urgency-contact { position: relative; flex-shrink: 0; }
.tsb-urgency-contact-menu {
	position: absolute;
	top: calc(100% + 8px);
	right: 0;
	z-index: 20;
	display: flex;
	flex-direction: column;
	min-width: 200px;
	background: var(--tsb-paper);
	border: 1px solid rgba(0,0,0,0.08);
	border-radius: 12px;
	box-shadow: 0 8px 30px rgba(0,0,0,0.12);
	overflow: hidden;
	padding: 6px;
}
.tsb-urgency-contact-option {
	appearance: none;
	-webkit-appearance: none;
	display: block;
	width: 100%;
	text-align: left;
	font: inherit;
	font-size: 14px;
	color: var(--tsb-modal-text, var(--tsb-ink));
	background: none;
	border: none;
	border-radius: 8px;
	padding: 10px 12px;
	margin: 0;
	cursor: pointer;
	text-decoration: none;
}
.tsb-urgency-contact-option:hover { background: var(--tsb-sand); }
/* v1.9.0 fix — Chloé reported the popover AND the message modal both stuck
   permanently open on Accueil. Root cause: `.tsb-urgency-contact-menu` sets
   `display: flex` unconditionally, and — being an author rule — that wins
   over the browser's own `[hidden]{display:none}` UA rule regardless of
   the `hidden` attribute JS toggles on it (same class of bug already fixed
   for `.tzb-step[hidden]` in Templiz Booking's widget). Same fix here:
   an explicit `[hidden]` override that actually hides it. */
.tsb-urgency-contact-menu[hidden] { display: none; }

/* On-site message modal (opened from the "Me contacter" popover) */
.tsb-message-modal {
	position: fixed;
	inset: 0;
	z-index: 100;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
	background: rgba(20,19,15,0.5);
}
/* Same `[hidden]` fix as the popover menu above — this modal was also
   stuck permanently visible for the same reason. */
.tsb-message-modal[hidden] { display: none; }
.tsb-message-modal-inner {
	position: relative;
	width: 100%;
	max-width: 420px;
	max-height: 90vh;
	overflow-y: auto;
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
	border-radius: 16px;
	padding: 32px 28px;
}
.tsb-message-modal-close {
	position: absolute;
	top: 14px; right: 14px;
	width: 30px; height: 30px;
	border-radius: 50%;
	border: 1px solid rgba(0,0,0,0.1);
	background: none;
	font-size: 18px;
	line-height: 1;
	cursor: pointer;
	color: var(--tsb-modal-text, var(--tsb-ink));
}
.tsb-message-modal-inner h3 { font-family: var(--tsb-font-display); font-size: 20px; margin: 0 0 18px; }
.tsb-message-form { display: flex; flex-direction: column; gap: 12px; }
.tsb-field-row { display: flex; gap: 12px; }
.tsb-field-row input { flex: 1; min-width: 0; }
.tsb-message-form input,
.tsb-message-form textarea {
	border: 1px solid rgba(0,0,0,0.15);
	border-radius: 8px;
	padding: 12px 14px;
	font: inherit;
	width: 100%;
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
}
.tsb-message-form textarea { min-height: 90px; resize: vertical; }
.tsb-message-status { font-size: 13px; margin: 0; }
.tsb-message-success { color: #2E6B2E; }
.tsb-message-error { color: #B3554A; }

/* v1.9.4 — Chloé (item #77) : pièce jointe optionnelle sur le formulaire de
   message, même gabarit que .tzb-attachment-* (templiz-booking, widget de
   réservation) pour rester visuellement cohérent. */
.tsb-message-attachment-field { display: flex; flex-direction: column; gap: 6px; margin-top: -4px; }
.tsb-message-attachment-label { font-size: 13px; color: var(--tsb-modal-text, var(--tsb-ink)); opacity: 0.7; }
.tsb-message-attachment-input { font-size: 13px; width: 100%; max-width: 100%; }
.tsb-message-attachment-preview { display: flex; align-items: center; gap: 10px; }
.tsb-message-attachment-preview[hidden] { display: none; }
.tsb-message-attachment-preview img { width: 56px; height: 56px; object-fit: cover; border-radius: 6px; border: 1px solid rgba(0,0,0,0.15); }
.tsb-message-attachment-remove {
	appearance: none; -webkit-appearance: none;
	border: 1px solid rgba(0,0,0,0.15); border-radius: 4px;
	background: var(--tsb-paper); color: var(--tsb-ink);
	width: 26px; height: 26px; line-height: 1; cursor: pointer;
}
.tsb-message-attachment-status { font-size: 12px; opacity: 0.7; margin: 0; }
.tsb-message-attachment-status.tsb-message-attachment-error { color: #B3554A; opacity: 1; }
@media (max-width: 640px) {
	.tsb-field-row { flex-direction: column; }
}

/* item #107 — Chloé: "pour le téléphone ajoute des indicatifs" — same
   flag + dial-code selector as Templiz Booking's own phone field
   (.tzb-phone-field), added by tsbEnhancePhoneField() in frontend.js to the
   message form and the formation registration form's tel input. The
   generic ".tsb-message-form input" / ".tsb-formation-form input" rules
   above only match <input> elements, not the new <select> — repeat the
   same border/radius/padding/colors here for it, and stop the wrapped
   <input>'s own width:100% from fighting the flex layout. */
.tsb-phone-field { display: flex; gap: 8px; }
.tsb-phone-field .tsb-phone-dial {
	flex: 0 0 auto;
	width: auto;
	max-width: 96px;
	border: 1px solid rgba(0,0,0,0.15);
	border-radius: 8px;
	padding: 12px 6px;
	font: inherit;
	font-size: 14px;
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
	cursor: pointer;
}
.tsb-phone-field input[type="tel"] { flex: 1; min-width: 0; }
@media (max-width: 400px) {
	.tsb-phone-field { flex-wrap: wrap; }
	.tsb-phone-field .tsb-phone-dial { max-width: none; flex: 1 1 100%; }
}

/* "Les plus populaires" preview actions (Accueil) */
.tsb-services-preview-actions { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.tsb-services-policies-link { font-size: 13px; text-decoration: underline; opacity: 0.8; color: var(--tsb-services-policy-text, inherit); }
/* 36e vague, retour Chloé — item 6 : couleur dédiée du bouton "Toutes les
   prestations" (contour, comme le CTA "Voir les prestations" du héros) —
   :not(.tsb-btn-primary) exclut le bouton "Réserver" qui partage la même
   classe .tsb-services-cta ailleurs sur cette page (voir section-services.php,
   variante pleine page "Prestations"), déjà couvert par cta_bg/cta_text. */
.tsb-services-cta:not(.tsb-btn-primary) { color: var(--tsb-services-cta-text, var(--tsb-ink)); border-color: var(--tsb-services-cta-text, var(--tsb-ink)); }

/* Hero — v1.5.0: full-bleed background photo + dark scrim overlay, closer
   to xo-hair-melun.netlify.app. Falls back to a solid --tsb-ink background
   (still on-brand for every palette) when no hero photo is set, so the
   light overlay text stays legible either way. */
.tsb-hero-full {
	position: relative;
	min-height: 640px;
	display: flex;
	align-items: flex-end;
	padding: 64px 6vw 72px;
	background: var(--tsb-ink);
	overflow: hidden;
}
.tsb-hero-bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* v1.8.0 — keeps the top of the photo (where a face usually is) in
	   frame on very short/tall crops instead of the default 50% 50%
	   center, on every screen size the hero's min-height can take. */
	object-position: 50% 25%;
}
.tsb-hero-scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(180deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.55) 75%, rgba(0,0,0,0.72) 100%);
}
.tsb-hero-content {
	position: relative;
	z-index: 1;
	max-width: 640px;
	color: #fff;
}
.tsb-hero-avatar {
	width: 44px; height: 44px;
	border-radius: 50%;
	object-fit: cover;
	margin-bottom: 16px;
	border: 1px solid rgba(255,255,255,0.6);
}
.tsb-hero-kicker { font-family: var(--tsb-font-body); color: rgba(255,255,255,0.85); margin: 0 0 12px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; }
.tsb-hero-title { font-family: var(--tsb-font-display); font-size: clamp(36px, 5.5vw, 64px); line-height: 1.05; margin: 0 0 16px; color: #fff; }
.tsb-hero-tagline { font-size: 18px; max-width: 42ch; margin: 0 0 22px; opacity: 0.92; }
.tsb-hero-badges { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 26px; padding: 0; }
/* 31e vague, retour Chloé — "Mes couleurs" : couleur des badges réglable
   (fond/bordure + texte) ; non réglé = traitement translucide blanc
   d'origine, inchangé. */
.tsb-hero-badge {
	font-size: 12px;
	padding: 7px 14px;
	border: 1px solid var(--tsb-badge-bg, rgba(255,255,255,0.5));
	border-radius: 999px;
	background: var(--tsb-badge-bg, rgba(255,255,255,0.08));
	color: var(--tsb-badge-text, inherit);
}
.tsb-hero-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 16px; }
/* 34e vague — couleur personnalisable, voir TSB_Templates::default_custom_colors() (le blanc par défaut restait pensé pour une photo héro sombre). */
.tsb-hero-instagram { color: var(--tsb-hero-instagram, #fff); font-size: 14px; text-decoration: none; opacity: 0.9; }
.tsb-hero-instagram:hover { opacity: 1; text-decoration: underline; }

.tsb-btn {
	display: inline-block;
	font-family: var(--tsb-font-body);
	font-size: 15px;
	padding: 14px 30px;
	text-decoration: none;
	border: 1px solid var(--tsb-ink);
	border-radius: 999px; /* v1.6.0 — Chloé : tous les CTA doivent être arrondis */
}
/* item #102 — Chloé: "Mes couleurs" custom CTA background/text. Unset on
   every built-in palette and on Personnalisé left blank, so this falls
   back to the exact same ink/paper pair as before this field existed. */
.tsb-btn-primary { background: var(--tsb-cta-bg, var(--tsb-ink)); color: var(--tsb-cta-text, var(--tsb-paper)); }
/* v1.9.0 — bumped to a 4-selector chain (.tsb-site .tsb-hero-full
   .tsb-hero-actions .tsb-btn-*) on purpose: several per-template files
   (nature/loft/studio) style ".tsb-btn-primary" directly at 3-selector
   specificity for use elsewhere on the site, and without this the hero's
   photo-optimized white/transparent treatment (and, before it, even the
   plain ".tsb-btn" background) got silently overridden by that — the bug
   behind "Politiques de réservation" rendering in the same accent purple
   as the other two hero buttons instead of standing out. Signature's own
   intentional beige hero button (template-signature.css) matches this same
   4-selector specificity and — loading after base.css — still wins the tie,
   so its differentiator is unaffected. */
.tsb-site .tsb-hero-full .tsb-hero-actions .tsb-btn-primary,
.tsb-site .tsb-hero-full .tsb-hero-actions .tsb-btn-outline {
	border-color: #fff;
}
/* 31e vague, retour Chloé — "thème par défaut atelier [...] les textes des
   CTA sont Noir mets les en blanc" — 1re tentative : le bouton restait un
   fond blanc à texte --tsb-ink (noir pour la plupart des palettes, dont
   Ivoire par défaut) UNIQUEMENT corrigé pour le cas "pas encore de photo de
   héros". 32e vague, retour Chloé — "le texte du CTA SUR LA PHOTO [...] est
   toujours en noir" : le fond blanc à texte --tsb-ink était donc bien
   intentionnel mais restait noir aussi avec une vraie photo (seul le cas
   sans photo avait été traité) — unifié ici : le bouton principal du héros
   passe systématiquement en couleur d'accent + texte blanc, avec ou sans
   photo, réglable via "Mes couleurs" (cta_bg/cta_text) comme n'importe quel
   autre CTA du site. L'accent est toujours une teinte distincte de l'ink
   dans chaque palette (voir class-tsb-templates.php), donc jamais de texte
   blanc sur fond blanc, contrairement à l'ancien fond blanc fixe qui,
   fonctionnant par contraste plutôt que par couleur choisie, ne pouvait pas
   accueillir un texte blanc sans devenir illisible. */
/**
 * 44e vague, retour Chloé — "Dans le bloc réservation [...] ajoute la
 * possibilité de modifier le fond et texte du bouton réserver" (groupe
 * "Photo héro", juste à côté du champ existant pour le bouton Instagram) :
 * ce bouton ("Prendre rendez-vous") ne partageait jusqu'ici QUE cta_bg/
 * cta_text, générique et commun à tous les boutons pleins du site (bandeau
 * CTA, formulaires avis/urgence, formations...) — mêmes 2 champs dédiés que
 * header_cta_bg/header_cta_text pour le bouton "Réserver" de l'en-tête,
 * retombant sur cta_bg/cta_text (puis l'accent du thème) si non réglés.
 */
.tsb-site .tsb-hero-full .tsb-hero-actions .tsb-btn-primary { background: var( --tsb-hero-cta-bg, var( --tsb-cta-bg, var( --tsb-accent ) ) ); color: var( --tsb-hero-cta-text, var( --tsb-cta-text, #fff ) ); }
/* 31e vague, retour Chloé — "Mes couleurs" : texte du CTA "Voir les
   prestations" (bouton contour du héros) réglable ; non réglé = blanc,
   inchangé. Sert aussi de couleur de bordure, pour garder un contour
   toujours assorti au texte plutôt qu'un blanc fixe autour d'un texte
   recoloré. */
.tsb-site .tsb-hero-full .tsb-hero-actions .tsb-btn-outline { background: transparent; color: var(--tsb-cta-outline-text, #fff); border-color: var(--tsb-cta-outline-text, #fff); }
/* v1.8.0 — "Politiques de réservation" hero CTA: a fixed warm alert color
   (not tied to the prestataire's palette) so it reads as "important, read
   this before you book" rather than blending in as a third neutral action.
   No template touches ".tsb-btn-urgent" specifically, so — now that the
   per-template files no longer style the bare ".tsb-btn" either (v1.9.0
   fix, see above) — this applies cleanly on every theme. */
/* 31e vague, retour Chloé — "Mes couleurs" : fond/texte du CTA "Politiques
   de réservation" réglables ; non réglé = orange fixe d'origine, inchangé
   (reste indépendant de l'accent du thème par défaut, comme prévu
   ci-dessus). */
.tsb-btn-urgent,
.tsb-hero-actions .tsb-btn-urgent {
	background: var(--tsb-cta-urgent-bg, #E2572B);
	border-color: var(--tsb-cta-urgent-bg, #E2572B);
	color: var(--tsb-cta-urgent-text, #fff);
}
.tsb-btn-urgent:hover { filter: brightness(0.9); }

/* Numbered highlights strip (Accueil only) */
/* 33e vague — couleurs dédiées (fond/titre/texte) au lieu du fond de page
   transparent + couleur de titre générique partagée avec le reste du site —
   voir TSB_Templates::default_custom_colors(). */
/* 35e vague, retour Chloé — item 1 : le fallback "transparent" laissait
   simplement voir le fond de page (--tsb-paper) en dessous quand le réglage
   est désactivé, ce qui n'est pas un bug en soi (résultat visuel identique)
   — mais le champ admin annonce "paper" comme valeur par défaut pour ce
   réglage (voir site-builder.php/view-minisite.php, $tsb_advanced_colors),
   donc le fallback CSS doit réellement pointer dessus plutôt que sur un mot-
   clé neutre, pour rester vrai si --tsb-paper change (palette personnalisée)
   ou si un thème redéfinit un jour le fond de page par défaut de la section. */
.tsb-highlights { padding: 56px 6vw; border-bottom: 1px solid var(--tsb-sand); background: var(--tsb-highlights-bg, var(--tsb-paper)); }
.tsb-highlights-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 32px;
}
.tsb-highlights .tsb-section-title { color: var(--tsb-highlights-title, var(--tsb-title, var(--tsb-ink))); }
.tsb-highlight-index { display: block; font-family: var(--tsb-font-display); font-size: 15px; color: var(--tsb-accent-text, var(--tsb-accent)); margin-bottom: 10px; }
.tsb-highlight-title { font-family: var(--tsb-font-display); font-size: 20px; margin: 0 0 8px; color: var(--tsb-highlights-title, var(--tsb-title, var(--tsb-ink))); }
.tsb-highlight-text { font-size: 14px; opacity: 0.8; margin: 0; color: var(--tsb-highlights-text, inherit); }

/* Infos pratiques — icon+chevron accordion */
.tsb-policies { padding: 60px 6vw; max-width: 800px; }
.tsb-policy-item {
	border: 1px solid var(--tsb-sand);
	border-left: 3px solid var(--tsb-sand);
	padding: 4px 20px;
	margin-bottom: 10px;
	transition: border-color 0.15s ease;
}
.tsb-policy-item[open] { border-left-color: var(--tsb-accent); }
.tsb-policy-question {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 16px 0;
	cursor: pointer;
	font-family: var(--tsb-font-display);
	font-size: 17px;
	list-style: none;
}
.tsb-policy-question::-webkit-details-marker { display: none; }
.tsb-policy-icon { color: var(--tsb-accent-text, var(--tsb-accent)); font-size: 8px; }
.tsb-policy-label { flex: 1; }
.tsb-policy-chevron { transition: transform 0.15s ease; opacity: 0.6; }
.tsb-policy-item[open] .tsb-policy-chevron { transform: rotate(180deg); }
.tsb-policy-answer { padding: 0 0 18px 20px; font-size: 15px; opacity: 0.85; }

/* v1.9.0 — Chloé: the full Services page groups by category, one
   horizontally-scrollable row per category, instead of pill tabs filtering
   a single grid (removed — see section-services.php/frontend.js). */
.tsb-services-category-row { margin-bottom: 36px; }
.tsb-services-category-row:last-of-type { margin-bottom: 28px; }
.tsb-services-category-title { font-family: var(--tsb-font-display); font-size: 19px; margin: 0 0 16px; }
.tsb-services-scroll {
	display: flex;
	gap: 16px;
	overflow-x: auto;
	scroll-snap-type: x proximity;
	padding-bottom: 6px;
	margin: 0 -6vw;
	padding-left: 6vw;
	padding-right: 6vw;
	/* Thin, unobtrusive scrollbar where supported — the row still scrolls
	   fine without it (touch/trackpad), this just avoids a bulky default
	   scrollbar sitting under every category on desktop. */
	scrollbar-width: thin;
}
.tsb-services-scroll .tsb-service-card {
	flex: 0 0 260px;
	scroll-snap-align: start;
}
@media (max-width: 640px) {
	.tsb-services-scroll .tsb-service-card { flex-basis: 78vw; }
}

/* Réserver — 2-column layout with sidebar */
.tsb-reserve-layout {
	display: grid;
	grid-template-columns: 1.3fr 0.9fr;
	gap: 40px;
	align-items: start;
}
.tsb-reserve-sidebar { display: flex; flex-direction: column; gap: 20px; }
.tsb-reserve-card { padding: 24px; background: var(--tsb-paper); border: 1px solid var(--tsb-sand); }
.tsb-reserve-card-muted { background: rgba(0,0,0,0.02); }
.tsb-reserve-card-title { font-family: var(--tsb-font-display); font-size: 18px; margin: 0 0 16px; }
/* 57e vague, retour Chloé (texte trop gros sur la page Réserver) : le
   paragraphe "À savoir" (issu de Réglages > Devis > "Infos utiles") n'avait
   aucune taille définie et retombait donc sur les 16px par défaut du
   navigateur — plus gros que le texte "Comment ça marche ?" juste
   au-dessus (14px) dans la même colonne. Alignée sur ce texte voisin. */
.tsb-reserve-card p { font-size: 14px; line-height: 1.6; margin: 0; }
.tsb-reserve-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.tsb-reserve-steps li { display: flex; gap: 12px; align-items: flex-start; font-size: 14px; }
.tsb-reserve-step-num {
	flex-shrink: 0;
	width: 22px; height: 22px;
	border-radius: 50%;
	background: var(--tsb-accent);
	color: var(--tsb-on-accent, var(--tsb-paper));
	font-size: 12px;
	display: flex; align-items: center; justify-content: center;
}
.tsb-reserve-infos-link { display: inline-block; margin-top: 10px; font-size: 13px; text-decoration: underline; }

@media (max-width: 900px) {
	.tsb-reserve-layout { grid-template-columns: 1fr; }
}

/* About */
/* 34e vague — couleurs dédiées (fond/texte), voir TSB_Templates::
   default_custom_colors(). Pas de --tsb-about-title : le titre (aperçu
   Accueil uniquement) reste sur .tsb-section-title, déjà couvert par
   --tsb-title. */
/**
 * 35e vague, retour Chloé — "le fond du bloc À propos doit prendre toute la
 * largeur de la page sur tous les appareils" : le max-width vivait jusqu'ici
 * directement sur la <section> (qui porte aussi le fond --tsb-about-bg), donc
 * dès qu'une couleur personnalisée était choisie, le bandeau de couleur
 * s'arrêtait à 900px et laissait apparaître le fond de page de part et
 * d'autre sur un écran plus large. Déplacé sur .tsb-about-body (le
 * texte/photo) juste en dessous : la section — et donc son fond — est
 * maintenant pleine largeur sur tous les appareils, seul le contenu lisible
 * reste centré à une largeur confortable.
 */
.tsb-about {
	padding: 60px 6vw;
	background: var(--tsb-about-bg, var(--tsb-paper));
	text-align: left;
}
/**
 * 38e vague, retour Chloé — "reste centré" (page À propos ET aperçu
 * Accueil), après le fix 37e vague ci-dessus (text-align:left sur .tsb-about
 * lui-même). Cause trouvée cette fois : .tsb-about-body avait `margin: 0
 * auto`, qui centre la BOÎTE elle-même (max-width: 900px) au milieu d'une
 * section pleine largeur (35e vague) — le texte À L'INTÉRIEUR de cette boîte
 * est bien aligné à gauche depuis le fix précédent, mais la boîte étant
 * elle-même centrée dans une section bien plus large, un texte court
 * (quelques lignes) donne visuellement l'impression que tout est centré.
 * `margin: 0` (au lieu de `0 auto`) ancre la boîte à gauche de la section à
 * la place — s'applique aussi à .tsb-about-with-photo .tsb-about-body juste
 * en dessous, qui n'a pas sa propre règle margin et héritait donc du même
 * centrage.
 */
.tsb-about-body { display: block; max-width: 900px; margin: 0; text-align: left; }
.tsb-about-text { font-size: 17px; opacity: 0.9; color: var(--tsb-about-text, inherit); text-align: left; }
/* 44e vague, retour Chloé — "modifier la couleur pour « En savoir plus -> »" : suivait auparavant about_text, champ dédié désormais (voir TSB_Templates::default_custom_colors()). */
.tsb-about-more { display: inline-block; margin-top: 16px; font-size: 14px; text-decoration: underline; color: var(--tsb-about-more-text, var(--tsb-about-text, inherit)); }
/* Optional photo next to the text (Chloé, v1.8.0 — "À propos" page only). */
.tsb-about-with-photo .tsb-about-body {
	display: grid;
	grid-template-columns: 1.1fr 0.9fr;
	gap: 44px;
	align-items: center;
	max-width: 1100px;
}
.tsb-about-photo img { width: 100%; height: 100%; max-height: 480px; object-fit: cover; border-radius: 12px; }
/* v1.9.0 — Chloé: prestataire-chosen photo side. Default DOM order already
   puts the text first / photo second (photo on the right); flipping is
   just swapping their grid `order` rather than touching markup order,
   which also means the mobile stacking rule below (photo always first on
   small screens) keeps working unchanged either way. */
.tsb-about-photo-left .tsb-about-text { order: 1; }
.tsb-about-photo-left .tsb-about-photo { order: 0; }
@media (max-width: 800px) {
	.tsb-about-with-photo .tsb-about-body { grid-template-columns: 1fr; }
	.tsb-about-photo, .tsb-about-photo-left .tsb-about-photo { order: -1; }
	.tsb-about-photo img { max-height: 320px; }
}

/* Gallery — no section title (Chloé, v1.8.0): a touch more breathing room
   at the top to compensate. */
.tsb-gallery { padding: 40px 6vw 70px; }
.tsb-gallery-grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 14px;
}
.tsb-gallery-item img { width: 100%; height: 240px; object-fit: cover; }

/* Social gallery — Instagram's official oEmbed blockquote (item #88/#98/#125) */
/* 33e vague — fond dédié (voir TSB_Templates::default_custom_colors()) — ce
   bloc n'avait jusqu'ici aucune couleur personnalisable, contrairement aux
   autres sections de la page d'accueil. */
.tsb-social-gallery { padding: 20px 6vw 70px; background: var(--tsb-social-bg, transparent); }
/* 34e vague — couleur de titre dédiée, voir TSB_Templates::default_custom_colors(). */
.tsb-social-gallery .tsb-section-title { color: var(--tsb-social-title, var(--tsb-title, var(--tsb-ink))); }
.tsb-instagram-grid {
	display: grid;
	/* item #166 (retour Chloé) — "réduit la dimension des vidéos pour que ce
	   soit plus présentable" : colonnes plus étroites (320px → 220px) pour
	   une vraie grille de galerie, plutôt que des vidéos quasi plein écran. */
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: 20px;
	align-items: start;
}
.tsb-instagram-embed-wrap {
	/* item #125 — Instagram's embed.js resizes its own iframe to fit the
	   post's real height (it doesn't need help from this CSS the way the
	   old height-locked iframe box did) — this wrapper only centers it and
	   caps its width. Before embed.js runs (or if it's ever blocked), the
	   blockquote's own fallback link below renders as plain, readable
	   text instead of being clipped inside a fixed-height box. */
	max-width: 300px;
	margin: 0 auto;
}
.tsb-instagram-embed-wrap .instagram-media {
	margin: 0 auto !important;
	min-width: 0 !important;
}

/* item #127 — autoplaying <video>, when TSB_Profile::instagram_video_source()
   managed to resolve one (see section-social-gallery.php); the oEmbed
   blockquote from #125 sits right underneath it in the same markup as a
   live fallback, just hidden by default via .tsb-ig-fallback — the video's
   own onerror handler is what reveals it again, in the (expected-to-be-rare
   but real) case the resolved video link has gone stale by the time a
   visitor's browser tries to actually play it. */
/* item #166 (retour Chloé) — "réduit la dimension des vidéos pour que ce
   soit plus présentable" : 540px/640px de haut rendait chaque vidéo quasi
   plein écran sur la page (surtout des Reels au format portrait 9:16) —
   ramené à une taille de vignette de galerie, cohérente avec la largeur de
   colonne réduite ci-dessus (tsb-instagram-grid). */
.tsb-instagram-video-wrap { max-width: 300px; margin: 0 auto; }
.tsb-instagram-video {
	display: block;
	width: 100%;
	max-height: 380px;
	border-radius: 8px;
	background: var(--tsb-ink, #14130F);
	object-fit: cover;
}
.tsb-instagram-video-wrap .tsb-ig-fallback { display: none; }
.tsb-instagram-video-wrap.tsb-ig-video-failed .tsb-instagram-video { display: none; }
.tsb-instagram-video-wrap.tsb-ig-video-failed .tsb-ig-fallback { display: block; }

@media (max-width: 640px) {
	/* item #132 — Chloé : "sur le front swipe vers la droite pour visualiser
	   les autres vidéos". La grille passait déjà à une seule colonne
	   empilée sur mobile (aucun défilement horizontal). Remplacée ici par
	   le même motif que .tsb-services-scroll ci-dessus (ligne
	   horizontale scrollable + scroll-snap, défilement tactile natif, pas
	   de JS nécessaire) — seule la grille (photos, "Nos réalisations")
	   garde son empilement vertical, cette section est la seule concernée. */
	.tsb-instagram-grid {
		display: flex;
		gap: 16px;
		overflow-x: auto;
		scroll-snap-type: x proximity;
		padding-bottom: 6px;
		margin: 0 -6vw;
		padding-left: 6vw;
		padding-right: 6vw;
		scrollbar-width: thin;
	}
	.tsb-instagram-grid .tsb-instagram-video-wrap,
	.tsb-instagram-grid .tsb-instagram-embed-wrap {
		/* item #166 — même réduction que sur desktop (85vw pouvait occuper
		   quasi tout l'écran en portrait), plafonnée par le max-width de
		   300px déjà posé plus haut. */
		flex: 0 0 60vw;
		max-width: 300px;
		margin: 0;
		scroll-snap-align: start;
	}
}

/* Booking */
.tsb-booking { padding: 60px 6vw 80px; background: var(--tsb-sand); }
/* item #105 — Chloé: "le bloc de réservation agrandi le un peu car on voit
   pas tout le cta confirmer la réservation". Templiz Booking's own 2-column
   layout (220px sidebar + content) only drops to a single column below a
   640px VIEWPORT width (its media query isn't container-aware) — squeezée
   dans un wrapper à 560px sur un écran de largeur bureau, le bouton
   "Confirmer la réservation" ne tenait plus à côté de "Retour" dans ce qui
   restait de la colonne de contenu et se faisait couper
   (.tzb-widget-inner a overflow:hidden). 560px -> 640px donnait à cette
   rangée assez de place sans toucher au CSS de Templiz Booking lui-même,
   partagé avec chaque autre intégration du widget (pas seulement cette
   page du site-builder).
 *
 * 63e vague, retour Chloé — "tu as agrandi la hauteur je parlais de la
 * longueur" (après le 62e vague, qui avait porté max-width de .tzb-widget
 * (booking-widget.css) de 860px à 960px) : cause racine trouvée — CE
 * wrapper, posé à 640px par l'item #105 ci-dessus bien AVANT que
 * .tzb-widget n'atteigne 860px puis 960px au fil des vagues suivantes,
 * était resté à 640px sans jamais être revu. Un wrapper plus étroit que
 * le contenu qu'il contient gagne toujours (le widget reste fluide à
 * 100% de SON conteneur, voir le docblock de max-width dans
 * booking-widget.css) : la largeur réellement affichée était donc
 * bloquée à 640px, quelle que soit la valeur de max-width côté
 * .tzb-widget — d'où un 62e vague sans aucun effet visible en largeur.
 * Remonté un temps à 960px pour suivre .tzb-widget.
 *
 * 64e vague, retour Chloé — "remets la taille initiale 640px mais de
 * forme rectangulaire pour que tout soit plus lisible" : après avoir vu le
 * panneau réellement à 960px (une fois le bug ci-dessus corrigé), retour
 * en arrière demandé — 960px jugé trop large/moins lisible, 640px
 * (la largeur qu'elle a toujours connue jusqu'ici, ce bug masquant toute
 * valeur supérieure) redevient la référence. Remis ici à 640px pour suivre
 * .tzb-widget (voir booking-widget.css, même revert) — les deux doivent
 * TOUJOURS rester synchronisés (c'est l'absence de synchro, pas la valeur
 * en elle-même, qui a causé le bug du 63e vague).
 */
.tsb-booking-widget-wrap { max-width: 640px; }
.tsb-booking-missing { opacity: 0.7; font-style: italic; }

/* Testimonials */
.tsb-testimonials { padding: 70px 6vw; }
/* item #133 — "bloc de la photo [+] X avis publiés" en tête de la page
   Avis dédiée, seulement quand une photo a été choisie (Mon Site > Avis). */
.tsb-testimonials-intro {
	display: flex;
	align-items: center;
	gap: 24px;
	margin-bottom: 32px;
}
.tsb-testimonials-photo {
	width: 96px;
	height: 96px;
	border-radius: 50%;
	object-fit: cover;
	flex-shrink: 0;
}
.tsb-testimonials-intro .tsb-section-title { margin-bottom: 6px; }
.tsb-testimonials-count { margin: 0; opacity: 0.75; font-size: 14px; }
.tsb-testimonials-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 24px;
}
.tsb-testimonial { margin: 0; padding: 24px; border: 1px solid var(--tsb-sand); }
.tsb-testimonial-text { font-size: 15px; margin: 0 0 12px; }
.tsb-testimonial-meta { display: flex; justify-content: space-between; font-size: 13px; opacity: 0.75; }
.tsb-testimonials-empty { opacity: 0.7; font-style: italic; margin: 0 0 32px; }

/* v1.9.6 — Chloé (item #89): review submission form on the dedicated Avis
   page — the piece that was missing entirely (the admin moderation UI
   already existed but had no way to receive anything). */
.tsb-testimonial-form-wrap { max-width: 480px; margin-top: 40px; }
.tsb-testimonial-form-title { font-family: var(--tsb-font-display); font-size: 20px; margin: 0 0 16px; font-weight: 500; }
.tsb-testimonial-form { display: flex; flex-direction: column; gap: 12px; }
.tsb-testimonial-form input, .tsb-testimonial-form textarea {
	padding: 12px 14px;
	border: 1px solid var(--tsb-sand);
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
	font-family: var(--tsb-font-body);
	font-size: 14px;
}
.tsb-testimonial-form textarea { resize: vertical; }
.tsb-testimonial-form-rating { display: flex; gap: 4px; }
.tsb-testimonial-star {
	background: none;
	border: none;
	cursor: pointer;
	font-size: 26px;
	line-height: 1;
	padding: 0 2px;
	color: var(--tsb-sand);
}
.tsb-testimonial-star.is-active { color: var(--tsb-accent-text, var(--tsb-accent)); }
.tsb-testimonial-form-status { font-size: 13px; margin: 0; }

/* Footer */
/* 31e vague, retour Chloé — "Mes couleurs" : fond et texte du pied de page
   réglables indépendamment (voir class-tsb-shortcode.php). */
.tsb-footer { padding: 60px 6vw 40px; background: var(--tsb-footer-bg, var(--tsb-sand)); color: var(--tsb-footer-text, var(--tsb-ink)); }
/* 46e vague, retour Chloé — "le bouton Instagram le texte est en bleu [...]
   pareil pour le numéro de téléphone, mail et réseaux sociaux figurant dans
   le pied de page" : ces liens n'avaient aucune règle de couleur dédiée, ils
   gardaient donc le bleu par défaut du navigateur pour un <a> au lieu de
   suivre le texte du pied de page comme le reste. Voir le docblock de
   TSB_Templates::default_custom_colors() (clé footer_links) pour le détail
   complet — réglable indépendamment de "Texte du pied de page" si besoin,
   sinon suit ce dernier automatiquement (donc plus jamais bleu par défaut). */
.tsb-footer a { color: var(--tsb-footer-links, var(--tsb-footer-text, var(--tsb-ink))); }
.tsb-footer-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 32px;
	margin-bottom: 40px;
}
.tsb-footer-heading { font-family: var(--tsb-font-display); font-size: 18px; margin: 0 0 12px; font-weight: 500; }
.tsb-footer-col p { margin: 0 0 6px; font-size: 14px; }
.tsb-hours-list, .tsb-social-list { list-style: none; margin: 0; padding: 0; font-size: 14px; }
.tsb-hours-list li { display: flex; justify-content: space-between; padding: 3px 0; max-width: 220px; }
.tsb-social-list li { padding: 3px 0; }
.tsb-footer-credit { font-size: 13px; opacity: 0.6; margin: 0; }
/* item #132 — liens légaux + "Site créé avec Templiz" (voir
   section-footer.php) restent visuellement discrets, dans la continuité
   de .tsb-footer-credit plutôt qu'en éléments séparés qui attireraient
   l'œil plus que les infos du prestataire lui-même au-dessus. */
.tsb-footer-legal a, .tsb-footer-powered { opacity: 0.85; }
.tsb-footer-powered { display: inline-block; margin-left: 8px; }
.tsb-footer-templiz-social { list-style: none; display: flex; gap: 14px; margin: 10px 0 0; padding: 0; font-size: 12px; opacity: 0.6; }
/* 68e vague, retour Chloé — lien "Gérer mon espace professionnel" (voir
   section-footer.php) : même registre discret que .tsb-footer-credit
   juste au-dessus, légèrement plus marqué (opacity plus haute, soulignage
   au survol) puisque c'est un vrai lien fonctionnel, pas juste un crédit. */
.tsb-footer-pro-link { font-size: 13px; opacity: 0.75; margin: 8px 0 0; }
.tsb-footer-pro-link a { text-decoration: underline; text-underline-offset: 2px; }

@media (max-width: 900px) {
	.tsb-hero-full { min-height: 520px; padding: 48px 6vw 40px; }
	.tsb-highlights-grid { grid-template-columns: 1fr; gap: 28px; }
	.tsb-gallery-grid { grid-template-columns: repeat(2, 1fr); }
	.tsb-testimonials-grid { grid-template-columns: 1fr; }
	.tsb-footer-grid { grid-template-columns: 1fr; }
}

/* v1.9.1 — Chloé: "adapte la disposition des éléments pour la banner
   principale sur mobile et tablette". Below 860px (same breakpoint as the
   nav collapsing to its hamburger) the 3 hero CTAs only wrapped onto their
   own lines by accident, at whatever width their own label happened to
   need — "Politiques de réservation" ended up close to full-width just
   because it's the longest label, while the other two stayed narrower and
   left-aligned, an inconsistent result that was never actually designed.
   Stacking them as full-width pills on purpose reads as a deliberate
   mobile/tablet layout instead. */
@media (max-width: 860px) {
	.tsb-hero-actions { flex-direction: column; align-items: stretch; gap: 12px; }
	.tsb-hero-actions .tsb-btn { text-align: center; width: 100%; }
	.tsb-hero-instagram { margin-top: 4px; }
}

/* v1.8.0 — the hero photo's crop stays balanced across more screen sizes:
   a shorter banner on small phones (where 520px felt very tall) and a
   taller one on very large/ultra-wide desktop screens, instead of a single
   fixed height everywhere in between. */
@media (max-width: 480px) {
	.tsb-hero-full { min-height: 440px; padding: 40px 6vw 32px; }
	.tsb-hero-badges { gap: 6px; margin-bottom: 20px; }
	.tsb-hero-tagline { font-size: 16px; margin-bottom: 18px; }
}
@media (min-width: 1440px) {
	.tsb-hero-full { min-height: 720px; }
}

/* Services */
/* 31e vague, retour Chloé — "Mes couleurs" : fond du bloc "Les plus
   populaires" (Accueil) — même règle sur la page Prestations complète,
   qui partage cette classe ; non réglé = fond transparent (--tsb-paper),
   comportement inchangé. */
.tsb-services { padding: 70px 6vw; background: var(--tsb-services-bg, transparent); }
/* 34e vague — couleurs dédiées (titre/intro/texte des cartes), voir
   TSB_Templates::default_custom_colors(). */
.tsb-services .tsb-section-title { color: var(--tsb-services-title, var(--tsb-title, var(--tsb-ink))); }
/**
 * 43e vague, retour Chloé — "le bloc « Les plus populaires », la typographie
 * du texte est trop grande, elle doit être similaire au bloc « À propos »" :
 * confirmé qu'il s'agit du texte d'intro (.tsb-services-intro), l'équivalent
 * de .tsb-about-text pour ce bloc-ci. Cette règle n'avait jamais de
 * font-size à elle — elle héritait donc de la taille de texte par défaut du
 * THÈME actif (variable d'un thème à l'autre, parfois nettement plus grande
 * que 17px), alors que .tsb-about-text fixe la sienne explicitement à 17px
 * et ne dépend donc jamais du thème. Alignée ici sur .tsb-about-text
 * (taille ET opacité) pour un rendu identique quel que soit le thème actif.
 */
.tsb-services-intro { max-width: 60ch; font-size: 17px; opacity: 0.9; margin: -16px 0 32px; color: var(--tsb-services-intro-text, inherit); }
/* 34e vague, retour Chloé — item 3 : "justifie le texte d'introduction des
   prestations sur la page d'accueil" — uniquement l'aperçu Accueil
   (.tsb-services-preview), comme demandé à l'époque.
   76e vague, retour Chloé — "accueil site prestataires pour Les plus
   populaires, ne justifie pas le texte" : retour en arrière volontaire sur
   l'item ci-dessus, le texte justifié créant des espaces irréguliers entre
   les mots peu lisibles sur un texte aussi court. Règle retirée — l'aperçu
   Accueil retrouve l'alignement à gauche habituel, déjà celui de la page
   Prestations complète. */

/**
 * 55e vague, retour Chloé — "si un code promo est ajouté et activé par un
 * prestataire il faut que cela soit visible sur [le] site pour les
 * clientes [...] ajoute cela de manière design" : un "ticket" par code
 * actif (voir TZB_Promos::get_active(), section-services.php), affiché
 * uniquement sur la page Prestations complète, juste au-dessus de la
 * liste. Repris du même vocabulaire visuel que .tsb-service-note plus bas
 * (fond teinté à --tsb-accent, même famille de couleur) mais sous forme
 * de coupon — une ligne pointillée sépare la réduction (grand chiffre) du
 * code lui-même (mis en valeur comme un champ à recopier), plutôt qu'un
 * simple bandeau de texte, pour que le code saute aux yeux avant l'étape
 * 4 du panneau de réservation où la cliente le saisit.
 */
.tsb-promo-banner { display: flex; flex-wrap: wrap; gap: 14px; margin: 0 0 28px; }
.tsb-promo-ticket {
	display: flex;
	align-items: center;
	gap: 16px;
	padding: 14px 20px;
	border: 1.5px dashed color-mix(in srgb, var(--tsb-accent) 55%, transparent);
	border-radius: 12px;
	background: color-mix(in srgb, var(--tsb-accent) 10%, var(--tsb-paper));
}
.tsb-promo-ticket-discount {
	flex-shrink: 0;
	font-family: var(--tsb-font-display);
	font-size: 24px;
	font-weight: 700;
	line-height: 1;
	color: var(--tsb-accent-text, var(--tsb-accent));
}
.tsb-promo-ticket-divider {
	flex-shrink: 0;
	align-self: stretch;
	width: 0;
	border-left: 1.5px dashed color-mix(in srgb, var(--tsb-accent) 55%, transparent);
}
.tsb-promo-ticket-info { display: flex; flex-direction: column; gap: 2px; }
.tsb-promo-ticket-label { font-size: 12px; opacity: 0.75; }
.tsb-promo-ticket-code {
	font-family: var(--tsb-font-mono, ui-monospace, 'Courier New', monospace);
	font-size: 15px;
	font-weight: 700;
	letter-spacing: 0.06em;
	color: inherit;
}
.tsb-promo-ticket-scope { font-size: 12px; opacity: 0.75; }
@media (max-width: 640px) {
	.tsb-promo-ticket { width: 100%; }
}

.tsb-services-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 24px;
	margin-bottom: 32px;
}
.tsb-service-card { border: 1px solid var(--tsb-sand); }
.tsb-service-photo, .tsb-service-photo-placeholder { height: 180px; }
/* item #132 — Chloé: "as-tu une autre idée pour que ce soit plus
   présentable" pour les prestations sans photo — un aplat de couleur plein
   cadre a été remplacé par un dégradé doux (toujours basé sur la couleur
   choisie par la prestataire, via les 3 variables posées inline par
   section-services.php) + une pastille "monogramme" reprenant la 1re
   lettre du nom de la prestation, plutôt qu'un bloc plat sans relief. */
.tsb-service-photo-placeholder {
	background: linear-gradient(135deg, var(--tsb-svc-color-light, #efe6d8) 0%, var(--tsb-svc-color, #d9c6a8) 100%);
	display: flex;
	align-items: center;
	justify-content: center;
	position: relative;
	overflow: hidden;
}
/* Soft sheen in the top-right corner for a little depth/texture, purely
   decorative — never covers enough of the badge to affect legibility. */
.tsb-service-photo-placeholder::before {
	content: "";
	position: absolute;
	top: -30%;
	right: -20%;
	width: 65%;
	height: 65%;
	background: radial-gradient(circle, rgba(255,255,255,0.45), rgba(255,255,255,0) 70%);
	pointer-events: none;
}
.tsb-service-photo-placeholder-monogram {
	position: relative;
	width: 58px;
	height: 58px;
	border-radius: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--tsb-svc-color, #d9c6a8);
	color: var(--tsb-svc-color-text, #ffffff);
	font-family: var(--tsb-font-display);
	font-size: 22px;
	box-shadow: 0 6px 16px rgba(0,0,0,0.14);
}
/* v1.9.2 — Chloé: "adapte la visibilité des photos" pour Les plus
   populaires. Root cause: `object-fit: cover` defaults to centering the
   crop (`object-position: 50% 50%`), but prestataire photos are typically
   portrait shots with the face near the top — center-cropping a tall
   photo into a short card cuts through the face/hair instead of the empty
   space below the shoulders. Biased toward the top so the face stays in
   frame at any card height. */
.tsb-service-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center top; }
.tsb-service-body { padding: 18px; }
.tsb-service-name { font-family: var(--tsb-font-display); font-size: 19px; margin: 0 0 8px; color: var(--tsb-services-card-text, inherit); }
.tsb-service-desc { font-size: 14px; opacity: 0.8; margin: 0 0 14px; color: var(--tsb-services-card-text, inherit); }
.tsb-service-meta { display: flex; justify-content: space-between; font-size: 14px; font-weight: 600; }
.tsb-services-cta { display: inline-block; }

/* item #163 (retour Chloé, inspiré de xo-hair-melun.netlify.app/services) */
.tsb-service-note {
	font-size: 13px;
	background: color-mix(in srgb, var(--tsb-accent) 12%, transparent);
	border-left: 3px solid var(--tsb-accent);
	padding: 8px 10px;
	margin: 0 0 14px;
	line-height: 1.4;
}
/**
 * 44e vague, retour Chloé — "Dans le bloc les plus populaires [...] ajoute la
 * possibilité de modifier le fond et texte du bouton réserver" : ce bouton
 * (une prestation individuelle, sur "Les plus populaires" ET sur la page
 * Prestations complète, qui partagent la même carte) n'avait jamais de
 * couleur à lui — fond toujours transparent, texte toujours "Texte" (ink),
 * hérités de .tsb-btn de base. Non réglé = strictement identique à avant.
 */
.tsb-service-cta {
	display: block;
	width: 100%;
	text-align: center;
	margin-top: 14px;
	padding: 8px 14px;
	font-size: 13px;
	background: var(--tsb-services-card-cta-bg, transparent);
	color: var(--tsb-services-card-cta-text, var(--tsb-ink));
	border-color: var(--tsb-services-card-cta-text, var(--tsb-ink));
}

/* "Les plus populaires" (Accueil preview) — smaller, lighter cards than the
   full Services page (Chloé, v1.8.0: "je veux pas que ça soit trop gros").
   v1.9.0: up to 4 featured services now (was 3), so the preview grid gets
   its own 4-column row instead of the full page's 3. */
.tsb-services-preview .tsb-services-grid { grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 26px; }
/* item #131 — Chloé: "les plus populaires sur mobile et tablette doit
   apparaître sur une ligne avec possibilité de swiper vers la droite" — was
   a 2-col then 1-col grid (stacking cards, no swipe). Below 1100px this now
   switches to the same horizontally-scrollable, snap-to-card row already
   used for category rows on the full Prestations page (.tsb-services-scroll
   in section-services.php) — one line, swipeable, no JS needed. */
@media (max-width: 1100px) {
	.tsb-services-preview .tsb-services-grid {
		display: flex;
		grid-template-columns: none;
		overflow-x: auto;
		scroll-snap-type: x proximity;
		gap: 16px;
		margin: 0 -6vw 26px;
		padding: 0 6vw 6px;
		scrollbar-width: thin;
	}
	.tsb-services-preview .tsb-services-grid .tsb-service-card {
		flex: 0 0 68vw;
		scroll-snap-align: start;
	}
}
@media (max-width: 640px) {
	.tsb-services-preview .tsb-services-grid .tsb-service-card { flex-basis: 78vw; }
}
/**
 * 31e vague, retour Chloé — "sur la page d'accueil, dans les plus
 * populaires les images doivent être plus grandes en hauteur pour être
 * visible" : v1.8.0 avait volontairement réduit cette hauteur (150px,
 * "je veux pas que ça soit trop gros") — Chloé revient dessus, les photos
 * de prestation n'étaient plus assez visibles. Relevée au-dessus même de
 * la page Prestations complète (180px, ligne au-dessus dans ce fichier),
 * pour un vrai gain de visibilité sur ce bloc mis en avant en haut de
 * page d'accueil plutôt qu'un simple retour à l'ancienne valeur.
 */
.tsb-services-preview .tsb-service-photo,
.tsb-services-preview .tsb-service-photo-placeholder { height: 220px; }
.tsb-services-preview .tsb-service-photo-placeholder-monogram { width: 46px; height: 46px; font-size: 18px; }
.tsb-services-preview .tsb-service-body { padding: 14px; }
.tsb-services-preview .tsb-service-name { font-size: 16px; margin: 0 0 4px; }
.tsb-services-preview .tsb-service-desc {
	font-size: 13px;
	margin: 0 0 10px;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.tsb-services-preview .tsb-service-meta { font-size: 13px; }

/**
 * 33e vague, retour Chloé — "les plus populaires : laisse la possibilité aux
 * prestataires de définir la taille du texte" (Mon Site → Prestations,
 * champ "Taille du texte"). N'affecte QUE ce bloc de la page d'accueil, pas
 * la page Prestations complète — voir section-services.php ($tsb_text_size_class,
 * ajouté uniquement quand $is_preview). Deux classes de sélecteur (au lieu
 * d'une seule) pour l'emporter sur .tsb-services-preview .tsb-service-name
 * ci-dessus, sans avoir à toucher à cette règle de base.
 */
.tsb-services-preview.tsb-services-text-large .tsb-service-name { font-size: 18px; }
.tsb-services-preview.tsb-services-text-large .tsb-service-desc,
.tsb-services-preview.tsb-services-text-large .tsb-service-meta { font-size: 14px; }
.tsb-services-preview.tsb-services-text-xlarge .tsb-service-name { font-size: 20px; }
.tsb-services-preview.tsb-services-text-xlarge .tsb-service-desc,
.tsb-services-preview.tsb-services-text-xlarge .tsb-service-meta { font-size: 15px; }

/* Formations (v1.9.5 — Chloé, item #83/#87) — même gabarit que Services. */
.tsb-formations { padding: 70px 6vw; }
.tsb-formations-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 24px;
	margin-bottom: 32px;
}
/**
 * 49e vague, retour Chloé — "ce sera 3 formations par ligne sur tablette et
 * desktop et sur mobile elles seront empilées" : jusqu'ici la grille passait
 * à 2 colonnes dès 900px (tablette) avant de s'empiler à 640px — désormais
 * 3 colonnes jusqu'à 640px inclus (aucune étape intermédiaire à 2), puis 1
 * seule colonne en dessous (mobile).
 */
@media (max-width: 640px) {
	.tsb-formations-grid { grid-template-columns: 1fr; }
}
.tsb-formation-card { border: 1px solid var(--tsb-sand); }
/* item 19 (33e vague, retour Chloé) — "augmente la hauteur de l'image" : 180px
   -> 240px (voir aussi section-formations.php pour la source 'large' au lieu
   de 'medium', qui corrige la pixellisation). */
/**
 * 61e vague, retour Chloé — item 2 : "Les photos attribuées aux formations
 * sur la page formations des prestataires doivent être bien cadré et
 * entièrement visible le format actuel rectangle ne permet pas de voir
 * l'image en entier." Premier essai : object-fit: contain + bandeau neutre
 * (--tsb-sand) de part et d'autre pour ne jamais rogner l'image.
 *
 * 62e vague, retour Chloé — item 1 : revirement sur cet essai — "je ne veux
 * pas d'arrière-plan (fond) pour faire plus simple change la dimension du
 * cadre au format carré." Retour à object-fit: cover (l'image remplit tout
 * le cadre, sans aucun fond visible), mais le cadre lui-même passe d'un
 * rectangle fixe de 240px de haut à un format CARRÉ (aspect-ratio 1/1, qui
 * suit automatiquement la largeur de la carte plutôt qu'une hauteur fixée en
 * dur) — un carré rogne beaucoup moins qu'un rectangle large la plupart des
 * photos de formation (généralement proches du carré ou portrait), d'où le
 * choix de Chloé plutôt que de garder contain. object-position reste lu
 * pour le réglage de cadrage existant côté prestataire (voir
 * section-formations.php, photo_focal).
 */
.tsb-formation-photo { aspect-ratio: 1 / 1; height: auto; }
.tsb-formation-photo img { width: 100%; height: 100%; object-fit: cover; object-position: center top; }
.tsb-formation-body { padding: 18px; }
.tsb-formation-name { font-family: var(--tsb-font-display); font-size: 19px; margin: 0 0 6px; }
.tsb-formation-meta { font-size: 13px; opacity: 0.7; margin: 0 0 10px; }
.tsb-formation-desc { font-size: 14px; opacity: 0.8; margin: 0 0 14px; }
.tsb-formation-footer { display: flex; justify-content: space-between; align-items: center; font-size: 14px; font-weight: 600; margin-bottom: 14px; flex-wrap: wrap; gap: 8px; }
.tsb-formation-seats { font-size: 12px; font-weight: 600; opacity: 0.75; }
.tsb-formation-seats-full { color: #B3554A; opacity: 1; }
/**
 * 48e vague, retour Chloé — "si la formation est en promo [...] on pourra
 * voir le prix en promo et prix initial" : prix promo mis en avant (couleur
 * d'accent, cohérent avec les badges), prix initial barré juste à côté,
 * plus petit et discret -- même esprit qu'un prix barré e-commerce classique.
 */
.tsb-formation-price-promo { color: var(--tsb-accent); font-weight: 700; }
.tsb-formation-price-original { font-size: 12px; font-weight: 500; opacity: 0.55; text-decoration: line-through; margin-left: 6px; }
/* 48e vague, retour Chloé -- "le prestataire devra indiquer s'il y a des
   frais ou non" : même registre discret que .tsb-formation-certif ci-dessus
   (CPF/Qualiopi), pas un encart qui alourdirait la carte. */
.tsb-formation-fees { color: #9A6B3B; }
/* retour Chloé (oct. 2026) — "acompte à payer [...] doit être visible sur le
   site" : même registre discret que .tsb-formation-fees ci-dessus, couleur
   d'accent pour que l'information ne se perde pas au milieu des badges
   CPF/Qualiopi mais sans alourdir la carte avec un encart. */
.tsb-formation-deposit { color: var(--tsb-accent); font-weight: 600; }
/* 46e vague, retour Chloé — "Détails" à côté de "S'inscrire" : les 2
   boutons partagent maintenant la ligne (voir .tsb-formation-actions
   ci-dessous) au lieu que "S'inscrire" prenne toute la largeur seul. */
.tsb-formation-open-btn { flex: 1; text-align: center; }

/* Modal d'inscription — même structure que .tsb-message-modal (Urgence). */
.tsb-formation-modal {
	position: fixed; inset: 0; z-index: 1000;
	background: rgba(0,0,0,0.5);
	display: flex; align-items: center; justify-content: center;
	padding: 20px;
}
.tsb-formation-modal[hidden] { display: none; }
.tsb-formation-modal-inner {
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
	border-radius: 12px;
	padding: 28px;
	max-width: 420px;
	width: 100%;
	position: relative;
}
.tsb-formation-modal-close {
	position: absolute; top: 14px; right: 14px;
	background: none; border: none; font-size: 22px; line-height: 1; cursor: pointer; color: inherit;
}
.tsb-formation-modal-title { font-family: var(--tsb-font-display); font-size: 20px; margin: 0 0 18px; padding-right: 24px; }
/* item #113 — full (untrimmed) Description, shown in the registration
   modal itself — previously only a 30-word teaser on the card, never
   repeated at the actual "about to register" moment. */
.tsb-formation-modal-desc { font-size: 14px; opacity: 0.8; white-space: pre-wrap; margin: -8px 0 18px; }
.tsb-formation-form { display: flex; flex-direction: column; gap: 12px; }
.tsb-formation-form input {
	border: 1px solid rgba(0,0,0,0.15);
	border-radius: 8px;
	padding: 12px 14px;
	font: inherit;
	width: 100%;
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
}
.tsb-formation-form-status { font-size: 13px; margin: 0; }
.tsb-formation-form-status.tsb-message-success { color: #2E6B2E; }
.tsb-formation-form-status.tsb-message-error { color: #B3554A; }
/* 61e vague, retour Chloé — item 4 : bouton "Accéder à mon espace client" affiché sous le message de succès de l'inscription à une formation (voir frontend.js). */
.tsb-formation-form-success-actions { margin-top: 12px; }
.tsb-formation-form-success-actions .tsb-btn { padding: 10px 20px; font-size: 14px; }

/* 44e vague, retour Chloe -- paiement en plusieurs fois / date au choix de
   la cliente / certification-CPF (voir section-formations.php et
   frontend.js). ".tsb-formation-date-row" reprend ".tsb-field-row" pour
   heriter du gap/responsive existant mais passe en colonne (le libelle
   au-dessus du champ, jamais cote a cote comme prenom/nom). */
/* 45e vague -- badge officiel "Mon compte formation" : largeur fixe raisonnable
   pour un badge affiche sur une carte de formation, hauteur automatique pour
   conserver ses proportions (458x241px a la source, jamais redimensionnee en
   amont -- voir section-formations.php) et eviter toute pixellisation. */
.tsb-formation-cpf-badge { display: block; width: 120px; height: auto; margin: 2px 0; }
/* 47e vague, retour Chloé -- badge officiel Qualiopi (1655x241px a la
   source, jamais redimensionne en amont -- voir section-formations.php),
   meme traitement que le badge CPF ci-dessus mais largeur adaptee a son
   format plus allonge pour rester lisible ("Qualiopi processus certifie"). */
.tsb-formation-qualiopi-badge { display: block; width: 150px; height: auto; margin: 2px 0; }
/* 48e vague, retour Chloé -- "la mention 'Organisme certifié' doit figurer
   juste avant le logo" : légende au-dessus du badge, même esprit que le
   texte de repli "Non éligible au CPF" ci-dessus (petite meta discrète). */
.tsb-formation-qualiopi-label { display: block; font-size: 12px; font-weight: 600; opacity: 0.75; margin-bottom: 2px; }
.tsb-formation-date-row { flex-direction: column; gap: 6px; position: relative; }
.tsb-formation-date-row label { font-size: 13px; opacity: 0.8; }
.tsb-formation-date-row[hidden] { display: none; }

/**
 * 50e vague, retour Chloé — "Choix dates formations corrige pour tous les
 * navigateurs doit prendre en compte la période" : petit calendrier maison
 * (voir le docblock de tsbRenderFormationCalendar() dans frontend.js) qui
 * remplace le calendrier natif du navigateur — même apparence de champ que
 * les autres (.tsb-formation-form input ci-dessus) mais c'est un <button>,
 * pas un <input>, pour ouvrir/fermer le calendrier au clic.
 */
.tsb-formation-date-btn {
	border: 1px solid rgba(0,0,0,0.15);
	border-radius: 8px;
	padding: 12px 14px;
	font: inherit;
	width: 100%;
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
	text-align: left;
	cursor: pointer;
}
.tsb-formation-date-calendar {
	position: absolute;
	top: calc(100% + 6px);
	left: 0;
	z-index: 30;
	width: 280px;
	max-width: calc(100vw - 40px);
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
	border: 1px solid rgba(0,0,0,0.12);
	border-radius: 10px;
	box-shadow: 0 8px 28px rgba(20,19,15,0.18);
	padding: 12px;
}
.tsb-formation-date-calendar[hidden] { display: none; }
.tsb-formation-date-calendar-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; font-size: 13px; font-weight: 600; }
.tsb-formation-date-nav { background: none; border: none; cursor: pointer; font-size: 15px; padding: 4px 8px; color: inherit; }
.tsb-formation-date-nav:disabled { opacity: 0.3; cursor: default; }
.tsb-formation-date-calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.tsb-formation-date-day {
	border: none; background: none; border-radius: 6px; padding: 6px 0;
	font-size: 13px; cursor: pointer; color: inherit;
}
.tsb-formation-date-day:hover { background: rgba(0,0,0,0.06); }
.tsb-formation-date-day-selected { background: var(--tsb-accent); color: var(--tsb-on-accent, #fff); font-weight: 700; }
.tsb-formation-date-day-disabled { opacity: 0.25; cursor: default; }
.tsb-formation-date-day-disabled:hover { background: none; }

/**
 * 46e vague, retour Chloé — "remplace par 'Paiement en plusieurs fois' avec
 * les badges [...] Fais quelque chose de design" : badge compact "Nx" dans
 * la couleur d'accent du site (cohérent quel que soit le thème/palette,
 * plutôt qu'une image de badge à la couleur fixe) + petite fenêtre "Détails"
 * qui reprend le récapitulatif complet des échéances retiré de la carte.
 */
.tsb-formation-installments-badge-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tsb-installments-badge {
	display: inline-flex; align-items: center; justify-content: center;
	min-width: 36px; height: 26px; padding: 0 9px;
	border-radius: 999px;
	background: var(--tsb-accent);
	color: var(--tsb-on-accent, #fff);
	font-weight: 700; font-size: 13px; letter-spacing: 0.01em;
	flex-shrink: 0;
}
.tsb-installments-badge-label { font-size: 13px; font-weight: 600; opacity: 0.85; }

.tsb-formation-actions { display: flex; align-items: stretch; gap: 10px; }
.tsb-formation-details-wrap { position: relative; flex-shrink: 0; }
.tsb-formation-details-btn { padding: 14px 18px; background: transparent; color: inherit; }
.tsb-formation-details-popover {
	position: absolute;
	bottom: calc(100% + 10px);
	left: 0;
	z-index: 20;
	width: 260px;
	max-width: calc(100vw - 40px);
	background: var(--tsb-paper);
	color: var(--tsb-modal-text, var(--tsb-ink));
	border-radius: 12px;
	box-shadow: 0 8px 28px rgba(20,19,15,0.18);
	padding: 16px 18px;
}
.tsb-formation-details-popover[hidden] { display: none; }
.tsb-formation-details-schedule { font-size: 14px; font-weight: 600; margin: 0 0 8px; }
.tsb-formation-details-note { font-size: 12.5px; opacity: 0.75; margin: 0; padding-right: 14px; }
.tsb-formation-details-close {
	position: absolute; top: 8px; right: 10px;
	background: none; border: none; font-size: 18px; line-height: 1; cursor: pointer; color: inherit; opacity: 0.6;
}
.tsb-formation-details-close:hover { opacity: 1; }
/**
 * 49e vague, retour Chloé — "pour le bouton détails sur mobile la fenêtre
 * est coupée" (capture jointe) : l'ancien correctif ci-dessous (right:0 au
 * lieu de left:0) restait ancré au bouton "Détails", qui peut se trouver
 * n'importe où dans la rangée d'actions de la carte — sur un écran étroit,
 * un popover de 260px ancré à un bouton proche du bord gauche ou du centre
 * déborde alors largement HORS de l'écran (c'est exactement ce que montrait
 * la capture : le contenu coupé à gauche). Remplacé par un positionnement
 * indépendant de la position du bouton : centré à l'écran (position: fixed,
 * left/right fixes en dur plutôt qu'un ancrage relatif), qui ne peut plus
 * déborder quel que soit l'endroit où se trouve la carte sur la page.
 */
@media (max-width: 480px) {
	.tsb-formation-details-popover {
		position: fixed;
		left: 16px;
		right: 16px;
		bottom: auto;
		top: 50%;
		transform: translateY(-50%);
		width: auto;
		max-width: none;
		max-height: 75vh;
		overflow-y: auto;
	}
}

/**
 * 47e vague, retour Chloé — "ajoute [...] un deuxième 'Nombre de fois' [...]
 * rends cela compréhensible" : choix entre les 2 options dans le formulaire
 * d'inscription, affiché/rempli par frontend.js (voir data-tsb-installments-*
 * sur .tsb-formation-card) — même esprit que .tsb-formation-date-row
 * ci-dessus (bloc caché par défaut, colonne).
 */
.tsb-formation-installments-choice-row { display: flex; flex-direction: column; gap: 8px; }
.tsb-formation-installments-choice-row[hidden] { display: none; }
.tsb-formation-installments-choice-label { font-size: 13px; opacity: 0.8; margin: 0; }
/**
 * 48e vague, retour Chloé — "bug sur la fenêtre contextuelle en termes
 * d'affichage et alignement du texte" : ".tsb-formation-form input"
 * (règle générique pour Prénom/Nom/Email/Téléphone/Date, voir plus haut)
 * s'appliquait AUSSI aux 2 boutons radio de ce bloc — son "width: 100%"
 * forçait chaque radio à s'étirer sur toute la largeur de la ligne,
 * écrasant le texte du label dans une colonne minuscule (d'où le texte
 * découpé mot par mot). Les radios sont maintenant explicitement exclues
 * de ce style de champ texte et reprennent une taille native normale.
 */
.tsb-formation-installments-choice-option {
	display: flex; flex-direction: row; align-items: flex-start; gap: 10px;
	border: 1px solid var(--tsb-border, rgba(20,19,15,0.15));
	border-radius: 10px; padding: 10px 12px; cursor: pointer;
	font-size: 14px; line-height: 1.4;
}
.tsb-formation-installments-choice-option input[type="radio"] {
	width: auto; flex: 0 0 auto; margin: 3px 0 0; padding: 0; border: none; background: none;
}
.tsb-formation-installments-choice-text { flex: 1 1 auto; min-width: 0; }

/* FAQ */
.tsb-faq { padding: 60px 6vw; max-width: 800px; }
.tsb-faq-item { border-bottom: 1px solid var(--tsb-sand); padding: 16px 0; }
.tsb-faq-question { font-family: var(--tsb-font-display); font-size: 17px; cursor: pointer; }
.tsb-faq-answer { padding-top: 10px; font-size: 15px; opacity: 0.85; }

/* Team — margin-bottom (not just padding) so the page background shows
   through as a visible gap before the footer, which uses the same sand
   background and would otherwise blend straight into it (Chloé, v1.8.0). */
/* 33e vague — fond/titre/texte dédiés (voir TSB_Templates::
   default_custom_colors()) au lieu du fond var(--tsb-sand) figé en dur et de
   la couleur de titre générique partagée avec le reste du site. */
.tsb-team { padding: 60px 6vw; background: var(--tsb-team-bg, var(--tsb-sand)); margin-bottom: 48px; }
.tsb-team .tsb-section-title { color: var(--tsb-team-title, var(--tsb-title, var(--tsb-ink))); }
.tsb-team-intro { opacity: 0.8; margin: -16px 0 28px; max-width: 60ch; color: var(--tsb-team-text, inherit); }
.tsb-team-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
	gap: 24px;
}
.tsb-team-member { text-align: center; }
.tsb-team-photo { width: 110px; height: 110px; border-radius: 50%; object-fit: cover; margin: 0 auto 10px; }
.tsb-team-photo-placeholder {
	display: flex; align-items: center; justify-content: center;
	background: var(--tsb-ink); color: var(--tsb-paper);
	font-family: var(--tsb-font-display); font-size: 32px;
}
.tsb-team-name { font-size: 14px; font-weight: 600; margin: 0; color: var(--tsb-team-text, inherit); }
/* item #114 — badge du membre désigné comme prestataire principal(e). */
.tsb-team-primary-badge {
	font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
	color: var(--tsb-accent); margin: 4px 0 0; opacity: 0.85;
}

/* Map */
/* v1.9.0 — Chloé: "mets un espace entre le bloc de réservation et nous
   trouver" — this section previously had 0 top padding, so on the
   Réserver page the map felt like it was butting straight up against the
   booking widget's card right above it. A little top padding here
   (rather than raising .tsb-booking's own bottom padding, which would
   also affect every other page order) creates a clear breathing gap. */
.tsb-map { padding: 40px 6vw 70px; }
.tsb-map-embed { border: 1px solid var(--tsb-sand); }
.tsb-map-embed iframe { width: 100%; height: 340px; border: none; display: block; }

@media (max-width: 900px) {
	.tsb-services-grid { grid-template-columns: 1fr; }
}

/*
 * item #6 (51e vague, retour Chloé) — "mets une flèche en bas à droite de la
 * page qui permet de revenir en haut de la page, j'ai cru l'apercevoir sur
 * la page de The Barbie Effect sur mobile." Le bouton natif d'Astra
 * ci-dessous (v1.9.2, déjà stylé "rond et noir" comme demandé à l'époque)
 * n'apparaissait pas de façon fiable : rien ne provisionne son réglage
 * customizer sur un nouveau site, et la page d'accueil du mini-site
 * (TSB_Frontend::render()) n'appelle même pas get_footer(), dont dépend son
 * rendu. Remplacé par .tsb-scroll-top ci-dessous (voir
 * tsbInitScrollTopButton() dans frontend.js), propre à Templiz et actif sur
 * toutes les pages quel que soit le template — le natif est donc
 * définitivement masqué pour ne jamais en afficher deux à la fois.
 */
#ast-scroll-top { display: none !important; }

/*
 * item #6 (51e vague) — bouton "retour en haut" maison. z-index 60 choisi
 * après vérification des autres position:fixed du site (modales à 1000, le
 * plus haut "vrai" widget flottant à 40) : au-dessus du contenu normal, sous
 * toute modale ouverte.
 *
 * item (52e vague, retour Chloé) — "je veux que la flèche soit comme celle
 * sur la photo en pj" : la photo montre un simple chevron blanc, sans fond
 * ni cercle visible (juste une ombre portée pour rester lisible sur
 * n'importe quel fond de page) — remplace le rond noir plein de la version
 * précédente. drop-shadow (sur le bouton entier, pas juste le trait) donne
 * le contour sombre qui garde le chevron visible même sur un fond clair.
 */
.tsb-scroll-top {
	position: fixed;
	right: 20px;
	bottom: 20px;
	z-index: 60;
	width: 44px;
	height: 44px;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	background: none;
	padding: 0;
	color: #fff;
	cursor: pointer;
	filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.45));
	opacity: 0;
	visibility: hidden;
	transform: translateY(8px);
	transition: opacity .2s ease, transform .2s ease;
}
.tsb-scroll-top.is-visible { opacity: 1; visibility: visible; transform: translateY(0); }
.tsb-scroll-top:hover { opacity: 0.75; }
.tsb-scroll-top svg { display: block; }
@media (max-width: 600px) {
	.tsb-scroll-top { right: 14px; bottom: 14px; width: 40px; height: 40px; }
}
