/* =====================================================================
   Trimbour Rénov — « Chaux, anthracite et taupe »
   ---------------------------------------------------------------------
   Le niveau de finition vient de manufacturevb.com : grand display en
   maigre, micro-libellés interlettrés, cartes à icône filaire, filets
   d'un pixel, rien d'approximatif. L'identité, elle, est propre à
   Trimbour et s'en écarte volontairement :

   | | manufacturevb | Trimbour Rénov |
   |---|---|---|
   | fond    | crème jaune #F7F2EA   | chaux neutre #F4F3F0 |
   | encre   | brun chaud #2A2320    | anthracite #2F3034    |
   | accent  | or #9A7B4A            | taupe du logo #938A7A  |
   | display | Cormorant Garamond    | Sentient |
   | emphase | fragment italique doré| fragment au taupe     |

   L'anthracite et le taupe viennent du logo officiel de Trimbour, pas
   d'un choix d'atelier : le site s'aligne sur la marque. Les deux sont
   relevés au pixel sur le PNG de la charte — #2D2F32 et #938A7A, et
   non le #A89B8B longtemps supposé. Le taupe de marque ne sort jamais
   du logotype : il ne donne que 3,07 contre la chaux, ce qu'un
   logotype a le droit d'être et pas une phrase.

   Le site n'a aucune photographie. C'est la contrainte qui commande
   toute la composition : le vide se remplit de matière, pas d'images —
   la carte de visite, les cartes à icône, les filets. Voir DESIGN.md.

   Le site est du HTML statique : plus de runtime, plus de React. Toute
   l'interactivité tient dans quarante lignes en ligne, en bas de
   index.html. Voir DESIGN.md, section « Poids et vitesse ».
   ===================================================================== */

/* ------------------------------- polices ---------------------------- */

@font-face {
  font-family: 'Sentient';
  src: url('./fonts/sentient-400.1.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Sentient';
  src: url('./fonts/sentient-400i.1.woff2') format('woff2');
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Sentient';
  src: url('./fonts/sentient-500.1.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Switzer';
  src: url('./fonts/switzer-400.1.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Switzer';
  src: url('./fonts/switzer-500.1.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}

/* ------------------------------- palette ---------------------------- */
/* Hexadécimal d'abord, OKLCH redéfini sous @supports : l'OKLCH est la
   source, l'hexadécimal la conversion. Le public est rural et tous âges,
   le matériel ancien doit rester servi. Aucun gris pur, aucun #000,
   aucun #fff : les neutres sont teintés vers le froid de l'anthracite. */

:root {
  --paper:     #F4F3F0;   /* la chaux, fond de tout le site */
  --paper-2:   #EBEAE5;   /* bandes en creux */
  --table:     #FBFAF9;   /* la table : surfaces posées dessus */
  --ink:       #2F3034;   /* l'anthracite du logo, texte et aplats */
  --ink-2:     #5B5C60;   /* texte secondaire */
  --ink-3:     #6C6D72;   /* annotations */
  --line:      #E1E0DB;   /* filets */
  --line-2:    #C9C7C1;   /* filets marqués */

  /* Le taupe du logo. Trois degrés, et la distinction n'est pas
     décorative : le taupe de marque ne donne que 2,3 contre la chaux.
     Un logotype a le droit d'être à ce contraste, une phrase non. */
  --taupe:      #6E6152;   /* taupe de texte : libellés, icônes, survols */
  --taupe-marque: #938A7A; /* le taupe exact du logo, relevé sur le PNG de la charte */
  --taupe-clair:#C9BFB1;   /* le taupe posé sur l'anthracite */
  --taupe-pale: #EDEAE4;   /* lavis très pâle, jamais sous du petit texte */

  --sur-ink:   #F4F3F0;   /* ce qui s'écrit sur un bloc d'encre */
  --sur-ink-2: #ADAEB2;

  --f-display: 'Sentient', Georgia, 'Times New Roman', serif;
  --f-texte:   'Switzer', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --sortie: cubic-bezier(.22, 1, .36, 1);
}

@supports (color: oklch(0 0 0)) {
  :root {
    --paper:     oklch(0.958 0.003 95);
    --paper-2:   oklch(0.928 0.004 95);
    --table:     oklch(0.982 0.002 95);
    --ink:       oklch(0.290 0.008 280);
    --ink-2:     oklch(0.470 0.006 280);
    --ink-3:     oklch(0.535 0.005 280);
    --line:      oklch(0.892 0.003 95);
    --line-2:    oklch(0.818 0.004 95);

    --taupe:        oklch(0.470 0.022 75);
    --taupe-marque: oklch(0.606 0.019 79);
    --taupe-clair:  oklch(0.795 0.020 78);
    --taupe-pale:   oklch(0.930 0.007 82);

    --sur-ink:   oklch(0.958 0.003 95);
    --sur-ink-2: oklch(0.735 0.005 280);
  }
}

/* --------------------------- jetons de surface ----------------------
   Les composants ne connaissent que ces neuf jetons. Une section change
   de monde en les redéfinissant, et rien d'autre n'est à réécrire. */

:root {
  --bg:     var(--paper);
  --fg:     var(--ink);
  --fg-2:   var(--ink-2);
  --fg-3:   var(--ink-3);
  --hair:   var(--line);
  --hair-2: var(--line-2);
  --mark:   var(--taupe);
  --mark-t: var(--taupe);
  --mark-v: var(--taupe);
}

/* La bande en creux : même monde, chaux plus dense — et l'encre
   d'annotation redescend d'un cran avec elle. Le fond se règle sur ce
   qu'on écrit dessus, jamais l'inverse : à --ink-3 sur --paper-2, les
   trois `.tb-note` de la page tombaient à 4,18, sous l'AA. Mesuré sur
   la page rendue. */
.tb-creux {
  --bg: var(--paper-2);
  --fg-3: #68696D;
  background: var(--bg);
}
@supports (color: oklch(0 0 0)) {
  .tb-creux { --fg-3: oklch(0.510 0.005 280); }
}

/* La table : la surface claire posée sur la chaux. */
.tb-table { --bg: var(--table); background: var(--bg); }

/* Le bloc d'encre : la seule inversion du site. */
.tb-encre {
  --bg:     var(--ink);
  --fg:     var(--sur-ink);
  --fg-2:   var(--sur-ink-2);
  --fg-3:   var(--sur-ink-2);
  --hair:   #43444A;
  --hair-2: #55575E;
  --mark:   var(--taupe-clair);
  --mark-t: var(--taupe-clair);
  --mark-v: var(--taupe-clair);
  background: var(--bg);
  color: var(--fg);
}
@supports (color: oklch(0 0 0)) {
  .tb-encre { --hair: oklch(0.365 0.007 280); --hair-2: oklch(0.435 0.008 280); }
}

/* ---------------------------------- base ---------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html {
  background: var(--paper);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

b, strong { font-weight: 500; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-texte);
  font-weight: 400;
  font-size: 16.5px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.tb-page { min-height: 100%; background: var(--paper); overflow-x: clip; }

p { text-wrap: pretty; }
img, svg { max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }

::selection { background: var(--taupe-pale); color: var(--ink); }

:focus-visible { outline: 2px solid var(--taupe); outline-offset: 3px; }

/* ------------------------------- trames ----------------------------- */

.tb-wrap {
  width: 100%;
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: clamp(22px, 4.6vw, 72px);
}

.tb-sec        { padding-block: clamp(66px, 7.6vw, 116px); }
.tb-sec--court { padding-block: clamp(44px, 4.8vw, 72px); }

/* L'en-tête est collant : une ancre doit s'arrêter en dessous, pas
   dessous. */
[id] { scroll-margin-top: 100px; }

/* Au moment du saut, une section pas encore révélée est décalée de 24 px
   vers le bas par son animation d'entrée. Le navigateur cale sur la
   position visible, puis la section remonte et passe sous l'en-tête. On
   ajoute donc ce décalage tant qu'elle n'est pas révélée : il vaut
   exactement la translation de [data-lever]. */
[data-lever]:not(.tb-vu) { scroll-margin-top: 124px; }

.tb-duo {
  display: grid;
  grid-template-columns: minmax(0, 1.28fr) minmax(0, 1fr);
  gap: clamp(34px, 4.6vw, 76px);
  align-items: start;
}
.tb-duo--r { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
.tb-duo--bas { align-items: end; }

.tb-pile { display: flex; flex-direction: column; align-items: flex-start; gap: 20px; }
.tb-pile--large { gap: clamp(24px, 2.4vw, 32px); }
.tb-pile--serre { gap: 12px; }

.tb-rang { display: flex; flex-wrap: wrap; align-items: center; gap: 22px; }

.tb-colle { position: sticky; top: 122px; align-self: start; }

/* ----------------------------- typographie -------------------------- */
/* Sentient est plus charpenté que le display de la maison voisine : à
   graisse et corps égaux il pèse davantage, donc l'échelle est un cran
   en dessous et le maigre remonte de 300 à 400. Le texte courant reste
   porté par Switzer, jamais par lui. */

.tb-h1 {
  margin: 0;
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(38px, 5.1vw, 76px);
  line-height: 1.06;
  letter-spacing: -.021em;
  text-wrap: balance;
}

.tb-h2 {
  margin: 0;
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(29px, 3.3vw, 48px);
  line-height: 1.1;
  letter-spacing: -.018em;
  text-wrap: balance;
}

.tb-h3 {
  margin: 0;
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(21px, 2vw, 28px);
  line-height: 1.25;
  letter-spacing: -.012em;
  text-wrap: balance;
}

/* La voix : un fragment en italique doré par grand titre, jamais deux.
   C'est le seul ornement du système, et il vient de la maison. */
/* L'emphase : le dernier fragment d'un grand titre passe au taupe, et
   c'est tout — ni italique, ni soulignement. L'italique coloré au
   milieu d'un titre est la signature de la maison voisine ; un trait
   posé en fond traversait la ligne suivante dès que le fragment passait
   à la ligne, ce qui arrive tout le temps sur trois lignes de display.
   La couleur seule ne casse jamais. */
.tb-vx { color: var(--mark-v); }

/* L'emphase arrive après le titre : le fragment monte à l'encre avec sa
   ligne, puis passe au taupe. On lit la phrase, ensuite on voit
   l'accent — c'est le seul mouvement qui porte du sens plutôt que de
   l'entrée, et il ne coûte qu'une transition de couleur. */
[data-lever] .tb-vx { color: var(--fg); }
[data-lever].tb-vu .tb-vx { color: var(--mark-v); }
[data-lever] .tb-vx { transition: color .85s var(--sortie) .4s; }

.tb-lead {
  margin: 0;
  font-weight: 400;
  font-size: clamp(16.5px, 1.2vw, 18px);
  line-height: 1.75;
  color: var(--fg-2);
  max-width: 46ch;
}

.tb-txt  { margin: 0; font-size: 16.5px; line-height: 1.75; color: var(--fg-2); max-width: 62ch; }
.tb-note { margin: 0; font-size: 14px; line-height: 1.6; color: var(--fg-3); }

.tb-lbl {
  font-family: var(--f-texte);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--mark-t);
}

/* Chiffres alignés partout où il y a un nombre : plusieurs serifs les
   sortent en bas-de-casse et le « 1 » de « 1 semaine » se lit alors
   comme une coquille. Le jeu tabulaire reste réservé aux nombres — sur
   de la prose française il élargit aussi la virgule décimale. */
.tb-tel, .tb-appel__n, .tb-barre__n, .tb-chiffre__v, .tb-hdr__tel {
  font-variant-numeric: lining-nums tabular-nums;
}

.tb-tel {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(25px, 2.2vw, 32px);
  color: var(--fg);
  border-bottom: 1px solid var(--hair-2);
  padding-bottom: 2px;
  transition: color .3s var(--sortie), border-color .3s var(--sortie);
}
.tb-tel:hover { color: var(--mark); border-bottom-color: var(--mark); }

/* --------------------------- le filet de section --------------------
   Un libellé doré posé sur un filet qui court jusqu'au bord, et qui
   s'étire depuis la gauche à l'entrée de la section. */

.tb-fil {
  display: flex;
  align-items: center;
  gap: 18px;
  margin: 0 0 clamp(26px, 3vw, 42px);
}
.tb-fil::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--hair-2);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.1s var(--sortie) .1s;
}
.tb-vu .tb-fil::after { transform: scaleX(1); }

.tb-tete {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: 18px 40px;
  margin-bottom: clamp(30px, 3.6vw, 50px);
}
.tb-tete .tb-h2 { max-width: 17ch; }
.tb-tete .tb-note { max-width: 28ch; text-align: right; }

/* ------------------------------- l'appel ----------------------------
   Rectangle d'encre à angles vifs, libellé en capitales interlettrées.
   C'est la maison : le geste attendu ne s'arrondit pas. */

.tb-appel {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 15px 28px 16px;
  background: var(--ink);
  color: var(--sur-ink);
  transition: background .3s var(--sortie);
}
.tb-appel:hover { background: #3E4046; }
.tb-appel svg { flex: none; color: var(--taupe-clair); }
.tb-appel__k {
  display: block;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--taupe-clair);
  margin-bottom: 2px;
}
.tb-appel__n { display: block; font-size: 18px; font-weight: 500; letter-spacing: .01em; line-height: 1.3; }

.tb-appel--grand { padding: 18px 34px 20px; }
.tb-appel--grand .tb-appel__n { font-size: clamp(19px, 1.7vw, 23px); }

.tb-appel--menu { padding: 11px 20px; white-space: nowrap; }
.tb-appel--menu .tb-appel__n { font-size: 12px; font-weight: 500; letter-spacing: .16em; text-transform: uppercase; }

/* Sur un bloc d'encre, le bouton s'inverse : il devient l'aplat clair. */
.tb-encre .tb-appel { background: var(--paper); color: var(--ink); }
.tb-encre .tb-appel:hover { background: #FFFFFC; }
.tb-encre .tb-appel svg,
.tb-encre .tb-appel__k { color: var(--taupe); }

.tb-renvoi {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-2);
  padding-bottom: 5px;
  border-bottom: 1px solid var(--hair-2);
  transition: color .3s var(--sortie), border-color .3s var(--sortie);
}
.tb-renvoi:hover { color: var(--mark); border-bottom-color: var(--mark); }

.tb-bouton {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  background: transparent;
  color: var(--fg-2);
  border: 1px solid var(--hair-2);
  cursor: pointer;
  transition: color .25s var(--sortie), border-color .25s var(--sortie);
}
.tb-bouton:hover { color: var(--mark); border-color: var(--mark); }
.tb-bouton--fait { color: var(--mark); border-color: var(--mark); }

/* ------------------------------ la marque --------------------------- */

/* La marque n'est plus reconstruite en typographie : monogramme et
   lettrage sont le tracé du logotype officiel, vectorisé depuis le
   fichier de la charte (quatre SVG, deux pour le fond clair, deux pour
   le fond d'encre). Une reconstruction en Switzer approchait les
   proportions mais pas les lettres — le M écarté, le O parfaitement
   circulaire et le R à jambe droite du lettrage d'origine n'existent
   dans aucune graisse de la fonte du site.

   Sur fond d'encre, la marque ne se blanchit pas au filtre : ses deux
   teintes SONT l'identité. Les variantes « nuit » ne remplacent que
   l'anthracite, qui s'y noierait, et gardent le taupe intact. */

.tb-marque {
  display: inline-flex;
  align-items: center;
  gap: clamp(11px, 1.2vw, 16px);
}
.tb-marque__m { width: clamp(30px, 2.8vw, 38px); height: auto; }
.tb-marque__t { width: clamp(116px, 11.6vw, 152px); height: auto; }

/* Au chargement, la marque se dévoile par balayage : le monogramme,
   puis le lettrage. Un cache et non une opacité — c'est le geste d'un
   trait qui se pose, et il fonctionne sur une image sans avoir à
   incorporer le SVG dans la page. */
.tb-marque--anime .tb-marque__m,
.tb-marque--anime .tb-marque__t {
  clip-path: inset(0 100% 0 0);
  animation: tb-balaye .85s var(--sortie) forwards;
}
.tb-marque--anime .tb-marque__m { animation-delay: .12s; }
.tb-marque--anime .tb-marque__t { animation-delay: .3s; }
@keyframes tb-balaye { to { clip-path: inset(0 -1% 0 0); } }

/* ------------------------------- en-tête ---------------------------- */

.tb-hdr {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}

/* La jauge de lecture, et l'ombre que l'en-tête prend une fois décollé
   du haut de page. Les deux sont pilotées par la position de
   défilement, sans une ligne de script. Là où les chronologies de
   défilement n'existent pas encore (Firefox), la jauge reste à zéro et
   l'en-tête garde son filet : rien ne manque, rien ne casse. */
.tb-hdr__jauge {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: var(--taupe);
  transform: scaleX(0);
  transform-origin: left;
}
@supports (animation-timeline: scroll()) {
  .tb-hdr__jauge {
    animation: tb-jauge linear both;
    animation-timeline: scroll(root block);
  }
  .tb-hdr {
    animation: tb-pose linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 130px;
  }
}
@keyframes tb-jauge { to { transform: scaleX(1); } }
@keyframes tb-pose { to { box-shadow: 0 1px 0 rgba(47, 48, 52, .04), 0 14px 28px -22px rgba(47, 48, 52, .45); } }
.tb-hdr__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 26px;
  min-height: 82px;
}

.tb-nav { display: flex; gap: clamp(13px, 1.4vw, 19px); }
.tb-nav a {
  position: relative;
  font-size: 14px;
  color: var(--ink-2);
  padding: 6px 0;
  transition: color .25s var(--sortie);
}
.tb-nav a::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--taupe);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .35s var(--sortie);
}
.tb-nav a:hover { color: var(--ink); }
.tb-nav a:hover::after { transform: scaleX(1); }

.tb-hdr__tel {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.tb-hdr__tel svg { color: var(--taupe); }

/* ------------------------------- le héros ---------------------------
   Titre pleine largeur. La version précédente le tenait dans une colonne
   de 55 % face à une carte plus haute et centrée contre lui : deux blocs
   sans une seule ligne commune. */

.tb-hero { padding-block: clamp(54px, 6.4vw, 104px) clamp(38px, 4.2vw, 62px); }
.tb-hero .tb-h1 { max-width: 17ch; }
.tb-hero .tb-lead { max-width: 46ch; }

/* La photographie du premier écran.

   `align-items: stretch` est tout le dispositif : la colonne d'image
   prend la hauteur EXACTE du bloc de texte, et les deux colonnes
   commencent et finissent sur la même ligne. C'est ce qui manquait à la
   version rejetée en août — une carte plus haute que le texte et
   centrée contre lui ne partage aucune ligne avec lui, et ça se lit
   comme un défaut de montage.

   Le recadrage est donc dicté par le texte, jamais l'inverse : d'où
   `object-fit: cover`. La porte est cadrée un peu haut (`40%`) parce
   que le bas de la photo n'est qu'une marche et du gravier. */
.tb-hero__duo {
  grid-template-columns: minmax(0, 1.42fr) minmax(0, 1fr);
  align-items: stretch;
}
.tb-hero__f {
  position: relative;
  margin: 0;
  min-height: 300px;
  overflow: clip;
  background: var(--paper-2);
}
/* L'image est SORTIE DU FLUX, et ce n'est pas un détail de mise en
   œuvre : en flux, sa hauteur intrinsèque (largeur × son propre
   rapport) participe au calcul de la rangée, et une photo plus haute
   que le texte étire la rangée au lieu de s'y plier. Le bloc de texte
   se retrouve alors ballotté dans une case trop grande — exactement le
   défaut de montage qu'on voulait éviter.

   Le piège est sournois : avec la porte (3/4) la hauteur intrinsèque
   tombait par hasard à la hauteur du texte, et le dispositif semblait
   marcher. Il a cassé au premier changement de photo, la salle d'eau
   étant en 9/16. Hors du flux, la rangée est toujours réglée par le
   texte, quelle que soit la photo qu'on y mette. */
.tb-hero__i {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
}

.tb-ouvre > * {
  opacity: 0;
  transform: translateY(14px);
  animation: tb-monte .9s var(--sortie) forwards;
}
.tb-ouvre > *:nth-child(1) { animation-delay: .05s; }
.tb-ouvre > *:nth-child(2) { animation-delay: .15s; }
.tb-ouvre > *:nth-child(3) { animation-delay: .25s; }
.tb-ouvre > *:nth-child(4) { animation-delay: .35s; }
@keyframes tb-monte { to { opacity: 1; transform: none; } }

/* Le titre du héros se lève ligne à ligne derrière un cache : chaque
   ligne est un bloc qui coupe, et le texte monte depuis dessous. Le
   titre n'a donc pas d'animation propre — sinon l'opacité du parent
   ferait remonter tout le bloc d'un coup, cache compris.

   Le cache mange les jambages : le padding le rouvre vers le bas et la
   marge négative rend la place prise. */
.tb-lignes { animation: none; opacity: 1; transform: none; text-wrap: nowrap; }
.tb-lignes > span {
  display: block;
  overflow: hidden;
  padding-bottom: .09em;
  margin-bottom: -.09em;
}
.tb-lignes > span > span {
  display: block;
  transform: translateY(105%);
  animation: tb-leve 1s var(--sortie) forwards;
}
.tb-lignes > span:nth-child(1) > span { animation-delay: .12s; }
.tb-lignes > span:nth-child(2) > span { animation-delay: .24s; }
@keyframes tb-leve { to { transform: none; } }

/* Le héros n'est pas un [data-lever] : son emphase prend le taupe par
   animation, une fois les deux lignes levées.

   Les DEUX animations doivent être déclarées ici. L'emphase est
   elle-même la ligne intérieure, et `animation` est un raccourci :
   n'en poser qu'une remplace le nom de l'autre, et la ligne reste
   derrière son cache. */
.tb-lignes .tb-vx {
  color: var(--fg);
  animation:
    tb-leve   1s   var(--sortie) .24s forwards,
    tb-accent .85s var(--sortie) 1s   forwards;
}
@keyframes tb-accent { to { color: var(--mark-v); } }

/* Sur téléphone, chaque groupe garde son cache et son entrée, mais
   reprend le droit de passer à la ligne : sous 340 px la première coupe
   ne tient plus, et un titre qui déborde vaut moins qu'une coupe
   déplacée. Ne PAS repasser les groupes en `inline` pour ça — ils se
   suivent sans blanc dans le balisage, et « réparer,remettre » se
   recolle. */
@media (max-width: 620px) {
  .tb-lignes { text-wrap: wrap; }
}

/* --------------------------- le bandeau de promesses -----------------
   Pas de carte, pas de surface blanche : les quatre engagements sont
   posés directement sur la chaux, tenus par des filets. Une table
   blanche en aurait fait un encadré d'interface ; les filets en font
   une composition.

   Tous les filets se tracent à l'entrée — les horizontaux depuis la
   gauche, les verticaux depuis le haut, colonne après colonne. C'est le
   même geste que le filet de section, à l'échelle du bandeau. */

.tb-promesses {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  padding-block: clamp(30px, 3.2vw, 46px);
}

.tb-promesses::before,
.tb-promesses::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--hair-2);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.15s var(--sortie);
}
.tb-promesses::before { top: 0; }
.tb-promesses::after  { bottom: 0; transition-delay: .14s; }
[data-lever].tb-vu .tb-promesses::before,
[data-lever].tb-vu .tb-promesses::after { transform: scaleX(1); }

.tb-promesse {
  --d: 0s;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 15px;
  padding-inline: clamp(20px, 2.4vw, 34px);
}
.tb-promesse:first-child { padding-left: 0; }
.tb-promesse:last-child  { padding-right: 0; }

/* Le filet qui sépare deux colonnes ne descend pas jusqu'aux bords : il
   respire, sinon il referme le bandeau comme une grille de tableau. */
.tb-promesse::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 1px;
  background: var(--hair);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .95s var(--sortie) var(--d);
}
.tb-promesse:first-child::before { content: none; }
[data-lever].tb-vu .tb-promesse::before { transform: scaleY(1); }

.tb-promesse__i { width: 26px; height: 26px; flex: none; color: var(--taupe); }

/* Les accroches suivent les titres de cartes de « Travaux » : même
   sans-serif, même graisse, même approche. Les deux blocs se touchent
   maintenant, et un serif ici rouvrait une série que la liste de lots
   venait de fermer.

   La taille descend d'un cran par rapport au display qu'elles avaient :
   à corps égal, la sans-serif de texte en 500 pèse plus lourd que la
   Sentient en 400, et le bandeau prenait le pas sur le H2. */
.tb-promesse__l {
  margin: 0;
  font-family: var(--f-texte);
  font-weight: 500;
  font-size: clamp(19px, 1.5vw, 22px);
  line-height: 1.22;
  letter-spacing: -.006em;
  color: var(--fg);
  text-wrap: balance;
}
.tb-promesse__d { margin: 0; font-size: 14px; line-height: 1.62; color: var(--fg-2); }

/* Les trois rangées — marque, accroche, détail — sont partagées par les
   quatre colonnes : sans ça, une accroche de deux lignes décale le
   détail de sa voisine. */
@supports (grid-template-rows: subgrid) {
  .tb-promesses { grid-template-rows: auto auto auto; }
  .tb-promesse {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 3;
    align-content: start;
    row-gap: 15px;
  }
}

/* Le sceau se pose sous le bandeau, sans filet ni fond : c'est la
   signature de la promesse, pas une cinquième colonne. */
.tb-sceau {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-top: clamp(18px, 1.8vw, 24px);
}
.tb-sceau svg { flex: none; width: 20px; height: 20px; margin-top: 1px; color: var(--taupe); }
.tb-sceau__t { margin: 0; font-size: 14px; line-height: 1.55; color: var(--fg-2); text-wrap: pretty; }
.tb-sceau__t b { color: var(--fg); font-weight: 500; }

/* Le contenu se compose colonne après colonne, en même temps que son
   filet. Le retard ne porte que sur l'apparition. */
[data-lever] .tb-promesse > *, [data-lever] .tb-sceau { opacity: 0; translate: 0 12px; }
[data-lever].tb-vu .tb-promesse > *,
[data-lever].tb-vu .tb-sceau { opacity: 1; translate: none; }
.tb-promesse > *, .tb-sceau {
  transition: opacity .75s var(--sortie) var(--d, 0s), translate .75s var(--sortie) var(--d, 0s);
}
.tb-promesse:nth-child(2) { --d: .08s; }
.tb-promesse:nth-child(3) { --d: .16s; }
.tb-promesse:nth-child(4) { --d: .24s; }
.tb-sceau { --d: .34s; }

/* -------------------------------- les icônes -------------------------
   Un trait taupe, jamais un aplat. Toutes partagent la grille de 24 et la
   même épaisseur : c'est ce qui les fait lire comme une famille et non
   comme une collection ramassée.

   La largeur n'est pas cosmétique : un <svg> à viewBox sans dimensions
   est un élément remplacé de 300 × 150 par défaut. Sans cette règle,
   chaque icône occupe toute la largeur de sa carte. */

.tb-icone {
  width: 30px;
  height: 30px;
  flex: none;
  color: var(--mark);
  transition: transform .45s var(--sortie);
}
.tb-icone--petite { width: 20px; height: 20px; }

/* --------------------------------- le renvoi -------------------------
   Le « en parler » des cartes. Sa flèche avance au survol de la carte,
   pas au sien : c'est la carte entière qui est cliquable. */

.tb-lien {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding-top: 6px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .17em;
  text-transform: uppercase;
  color: var(--mark-t);
}
.tb-lien svg { flex: none; width: 14px; height: 14px; transition: transform .4s var(--sortie); }

/* --------------------------------- les cartes -------------------------
   Icône filaire, titre en display, deux lignes, un renvoi. Trois
   variantes, parce qu'une grille de quinze tuiles rigoureusement
   identiques est la signature de ce qui est généré. */

.tb-cartes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
  gap: 14px;
}
.tb-cartes--cinq { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }

.tb-carte {
  --d: 0s;
  display: flex;
  flex-direction: column;
  gap: 11px;
  padding: 25px 24px 23px;
  background: var(--table);
  border: 1px solid var(--line);
  transition:
    transform    .4s var(--sortie),
    box-shadow   .4s var(--sortie),
    border-color .4s var(--sortie),
    opacity      .7s var(--sortie) var(--d),
    translate    .7s var(--sortie) var(--d);
}
/* L'intitulé des cartes est en GROTESQUE, pas en display, et c'est une
   demande explicite de Tristan : « moins calligraphié, plus brut ». Le
   serif reste la voix des grands titres et des mesures ; dans un
   encadré de service, il se lisait comme un ornement là où le client
   attend une étiquette. Les deux grilles de cartes — travaux et
   interlocuteurs — partagent cette règle.

   C'est la première exception à « un intitulé est toujours en Sentient » ;
   les accroches des promesses, juste dessous, la suivent désormais.
   Elle est assumée : la règle vient de l'atelier, la demande vient de
   celui dont c'est le métier qui est décrit. */
.tb-carte__t {
  margin: 0;
  font-family: var(--f-texte);
  font-weight: 500;
  font-size: 17px;
  line-height: 1.3;
  letter-spacing: -.006em;
  color: var(--ink);
}
.tb-carte__d { margin: 0; font-size: 14px; line-height: 1.6; color: var(--ink-2); }

.tb-carte:hover {
  transform: translateY(-3px);
  border-color: var(--line-2);
  box-shadow: 0 2px 4px rgba(47, 48, 52, .04), 0 22px 42px -26px rgba(47, 48, 52, .30);
}
.tb-carte:hover .tb-icone { transform: translateY(-2px); }
.tb-carte:hover .tb-lien svg { transform: translateX(5px); }

/* La carte d'anthracite : l'aplat de marque, deux par série au plus. */
.tb-carte--encre {
  --mark: var(--taupe-clair);
  --mark-t: var(--taupe-clair);
  background: var(--ink);
  border-color: var(--ink);
}
.tb-carte--encre .tb-carte__t { color: var(--sur-ink); }
.tb-carte--encre .tb-carte__d { color: var(--sur-ink-2); }
.tb-carte--encre:hover { border-color: var(--ink); }

/* La carte ouverte ferme la série : trait interrompu, fond de chaux — et
   elle la ferme littéralement. Quinze cartes sur quatre colonnes laissent
   une case vide au dernier rang ; celle-ci en prend deux et le rang se
   referme. Le document décrivait ce comportement depuis trois versions,
   la règle n'existait que dans l'adaptation mobile : le dernier rang
   restait ouvert sur tous les écrans larges.

   La borne n'est pas décorative : la grille est en `auto-fit` et ne
   passe à quatre colonnes qu'au-delà de ~1 140 px. Sur trois colonnes,
   une carte de deux cases ne tiendrait pas dans le rang entamé et
   creuserait deux trous au lieu d'un. */
@media (min-width: 1160px) {
  .tb-carte--large { grid-column: span 2; }
}
.tb-carte--ouverte { background: transparent; border-style: dashed; border-color: var(--line-2); }
.tb-carte--ouverte .tb-carte__t { color: var(--taupe); }
.tb-carte--ouverte:hover { border-color: var(--taupe); }

/* Cascade d'entrée : chaque colonne part un cran après la précédente,
   ce qui donne une diagonale et non une vague. Le retard ne porte que
   sur l'apparition (opacity et translate) — le survol, porté par
   transform, répond tout de suite. */
[data-lever] .tb-carte { opacity: 0; translate: 0 16px; }
[data-lever].tb-vu .tb-carte { opacity: 1; translate: none; }
.tb-carte:nth-child(4n+2) { --d: .07s; }
.tb-carte:nth-child(4n+3) { --d: .14s; }
.tb-carte:nth-child(4n+4) { --d: .21s; }

/* ------------------------------- les chantiers ------------------------
   Mosaïque en colonnes CSS : le navigateur équilibre les hauteurs et
   chaque photo garde ses proportions. Une grille à hauteur fixe aurait
   recadré la chambre, qui est un plan large, ou la salle d'eau, qui est
   un portrait — c'est-à-dire perdu ce qui fait leur intérêt.

   La légende sous la grille se limite au lieu. La phrase complète est
   réservée à la visionneuse : une grille couverte de phrases se lit
   mal, on regarde des images. */

/* Deux colonnes et non trois. La mosaïque en portait trois quand il y
   avait six photographies ; il en reste cinq, dont deux portraits très
   hauts, et sur trois colonnes la première partait deux fois plus bas
   que les autres. Deux colonnes rééquilibrent et, surtout, montrent
   plus grand — c'est le sens de la sélection : moins de photos, mieux
   vues. */
.tb-vues { columns: 2; column-gap: clamp(15px, 1.6vw, 22px); }

.tb-vue {
  --d: 0s;
  break-inside: avoid;
  margin: 0 0 15px;
  opacity: 0;
  translate: 0 18px;
  transition: opacity .8s var(--sortie) var(--d), translate .8s var(--sortie) var(--d);
}
[data-lever].tb-vu .tb-vue { opacity: 1; translate: none; }
.tb-vue:nth-child(2) { --d: .08s; }
.tb-vue:nth-child(3) { --d: .16s; }
.tb-vue:nth-child(4) { --d: .24s; }
.tb-vue:nth-child(5) { --d: .32s; }
.tb-vue:nth-child(6) { --d: .40s; }

/* Le bouton est le cadre : l'image entière est la cible, et c'est un
   vrai bouton, donc atteignable au clavier. */
.tb-vue__b {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: var(--paper-2);
  overflow: clip;
  cursor: zoom-in;
}
.tb-vue__b img {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 1.1s var(--sortie);
}
.tb-vue__b:hover img { transform: scale(1.04); }

.tb-vue__c {
  margin-top: 10px;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* -------------------------------- la visionneuse ---------------------
   Plein écran, fond d'encre presque opaque. Une seule image à la fois,
   contenue et jamais recadrée : c'est tout l'intérêt d'agrandir. */

.tb-loupe {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: clamp(52px, 6vw, 80px) clamp(16px, 8vw, 110px) clamp(76px, 8vw, 104px);
  /* Opaque, et non presque : à 96 % la page chaux transparaît encore
     nettement derrière, et on regarde une photo, pas un calque. */
  background: #17181A;
  opacity: 0;
  transition: opacity .38s var(--sortie);
}
.tb-loupe--on { opacity: 1; }
.tb-loupe[hidden] { display: none; }

.tb-loupe__c { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 0; max-height: 100%; }

.tb-loupe__i {
  display: block;
  max-width: 100%;
  max-height: calc(100vh - clamp(160px, 18vw, 220px));
  width: auto;
  height: auto;
  object-fit: contain;
  opacity: 0;
  transition: opacity .3s var(--sortie);
}
.tb-loupe--prete .tb-loupe__i { opacity: 1; }

.tb-loupe__l {
  position: absolute;
  left: 0; right: 0; bottom: clamp(22px, 3vw, 34px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  padding-inline: 20px;
  text-align: center;
}
.tb-loupe__t {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--taupe-clair);
}
.tb-loupe__d { font-size: 14px; line-height: 1.5; color: #C6C7CA; max-width: 54ch; }
.tb-loupe__n { font-size: 11.5px; letter-spacing: .16em; color: #8E9094; font-variant-numeric: tabular-nums; }

/* Les commandes : cercles discrets, jamais posés sur l'image. */
.tb-loupe__f, .tb-loupe__p, .tb-loupe__s {
  position: absolute;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid rgba(244, 243, 240, .22);
  border-radius: 50%;
  background: transparent;
  color: #F4F3F0;
  cursor: pointer;
  transition: border-color .25s var(--sortie), background .25s var(--sortie);
}
.tb-loupe__f:hover, .tb-loupe__p:hover, .tb-loupe__s:hover {
  border-color: rgba(244, 243, 240, .55);
  background: rgba(244, 243, 240, .08);
}
.tb-loupe__f { top: clamp(14px, 2vw, 26px); right: clamp(14px, 2vw, 26px); }
.tb-loupe__p { left: clamp(10px, 2.4vw, 34px); top: 50%; translate: 0 -50%; }
.tb-loupe__s { right: clamp(10px, 2.4vw, 34px); top: 50%; translate: 0 -50%; }

@media (max-width: 760px) {
  .tb-vues { columns: 1; column-gap: 12px; }
  .tb-vue { margin-bottom: 12px; }
  /* Au doigt, on fait glisser : les flèches descendent au niveau du
     pouce plutôt que de rester au milieu des bords. */
  .tb-loupe__p, .tb-loupe__s { top: auto; bottom: clamp(96px, 15vw, 130px); translate: none; width: 44px; height: 44px; }
  .tb-loupe__p { left: 16px; }
  .tb-loupe__s { right: 16px; }
  .tb-loupe__d { font-size: 13px; }
}
@media (max-width: 460px) {
  .tb-vues { columns: 1; }
}

/* ------------------------------- le portrait -------------------------
   C'est l'image qui compte le plus : on confie un chantier à quelqu'un,
   pas à une entreprise. Elle est arrivée le 17 août 2026 et a remplacé
   le monogramme « TT » qui tenait la place.

   Le recadrage au 4/5 est fait à l'encodage, pas ici : le sujet n'est
   pas au centre de l'original, et un `object-fit: cover` l'aurait
   décentré différemment à chaque largeur. Le `cover` ci-dessous ne sert
   plus qu'à garantir le cadre si un jour on y dépose une autre photo.

   Les règles du monogramme (`__m`) restent : le cadre doit pouvoir
   retomber sur un élément dessiné plutôt que sur un trou. */

.tb-portrait { margin: 0; position: sticky; top: 116px; }

.tb-portrait__i,
.tb-portrait__m {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  border: 1px solid var(--line);
  background: var(--taupe-pale);
}
/* `height: auto` n'est pas cosmétique. Les attributs width/height d'une
   <img> sont posés par la feuille du navigateur comme indications de
   présentation : l'auteur écrase `width` avec son `width: 100%`, mais
   sans `height` déclarée, l'indication `height: 1063px` subsiste et
   l'emporte sur `aspect-ratio`. Le portrait s'étirait alors à
   451 × 1063 au lieu de 451 × 564 — mesuré. */
.tb-portrait__i { height: auto; object-fit: cover; }

.tb-portrait__m { position: relative; display: grid; place-items: center; }
.tb-portrait__m::after {
  content: '';
  position: absolute;
  inset: 13px;
  border: 1px solid var(--taupe);
  opacity: .20;
}
.tb-portrait__m span {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(52px, 6.4vw, 82px);
  letter-spacing: .08em;
  text-indent: .08em;
  line-height: 1;
  color: var(--taupe);
}
.tb-portrait__c {
  margin-top: 13px;
  font-size: 12.5px;
  letter-spacing: .02em;
  color: var(--fg-3);
}

/* ------------------------------- la signature ------------------------
   Un filet vertical d'un pixel, comme un trait de plume en marge. */

.tb-signature {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding-left: 17px;
  border-left: 1px solid var(--mark);
}
.tb-signature__n {
  font-family: var(--f-display);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -.01em;
  color: var(--fg);
}
.tb-signature__r { font-size: 13px; color: var(--fg-3); }

/* ------------------------------- le déroulé -------------------------- */

.tb-etapes { display: grid; border-top: 1px solid var(--hair); width: 100%; }
.tb-etapes__r {
  display: flex;
  align-items: flex-start;
  gap: 15px;
  padding: 16px 0;
  border-bottom: 1px solid var(--hair);
}
.tb-etapes__r .tb-icone { margin-top: 1px; }
.tb-etapes__t { font-size: 15.5px; line-height: 1.6; color: var(--fg-2); }

/* -------------------------------- les avis ---------------------------
   Cartes claires, guillemet doré en tête, citation en italique display.
   Le « G » de Google garde ses vraies couleurs : ce n'est pas un
   ornement, il dit d'où vient l'avis, et c'est ce qui lui donne son
   poids. */

.tb-avis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
  gap: 14px;
}
.tb-avis__i {
  --d: 0s;
  display: flex;
  flex-direction: column;
  gap: 13px;
  padding: 24px 24px 22px;
  background: var(--table);
  border: 1px solid var(--line);
  transition: opacity .7s var(--sortie) var(--d), translate .7s var(--sortie) var(--d);
}
.tb-avis__i:nth-child(3n+2) { --d: .08s; }
.tb-avis__i:nth-child(3n+3) { --d: .16s; }
[data-lever] .tb-avis__i { opacity: 0; translate: 0 16px; }
[data-lever].tb-vu .tb-avis__i { opacity: 1; translate: none; }

.tb-avis__q {
  display: block;
  height: 22px;
  font-family: var(--f-display);
  font-size: 46px;
  line-height: 1;
  color: var(--taupe);
}
.tb-avis__t {
  margin: 0;
  font-family: var(--f-display);
  font-style: italic;
  font-weight: 400;
  font-size: 21px;
  line-height: 1.45;
  color: var(--ink);
}
.tb-avis__m { display: flex; flex-direction: column; gap: 2px; margin-top: auto; }
.tb-avis__n { font-size: 14.5px; font-weight: 500; color: var(--ink); }
.tb-avis__d { font-size: 12.5px; color: var(--ink-3); }
.tb-etoiles { display: inline-flex; gap: 3px; color: var(--taupe); }
.tb-avis__src {
  display: inline-flex; align-items: center; gap: 7px;
  padding-top: 14px; border-top: 1px solid var(--line);
  font-size: 12px; color: var(--ink-3);
}
.tb-avis__src svg { width: 13px; height: 13px; }

/* ------------------------------ les questions ------------------------ */

.tb-q { border-top: 1px solid var(--hair); }
.tb-q__i { border-bottom: 1px solid var(--hair); }
.tb-q__h { margin: 0; font: inherit; }
.tb-q__r {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 22px;
  width: 100%;
  padding: 18px 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}
.tb-q__q {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(19px, 1.6vw, 23px);
  line-height: 1.35;
  color: var(--fg);
  transition: color .25s var(--sortie);
}
.tb-q__r:hover .tb-q__q { color: var(--mark); }
.tb-q__c { flex: none; color: var(--mark); transition: transform .4s var(--sortie); }
.tb-q__i--on .tb-q__c { transform: rotate(180deg); }
.tb-q__w { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .45s var(--sortie); }
.tb-q__i--on .tb-q__w { grid-template-rows: 1fr; }
.tb-q__w > div { overflow: hidden; }
.tb-q__a { margin: 0; padding: 0 0 22px; font-size: 15.5px; line-height: 1.72; color: var(--fg-2); max-width: 62ch; }

/* -------------------------------- la fiche --------------------------- */

.tb-fiche { border-top: 1px solid var(--hair); width: 100%; }
.tb-fiche__r {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  padding: 13px 0;
  border-bottom: 1px solid var(--hair);
}
.tb-fiche__k { font-size: 10.5px; font-weight: 500; letter-spacing: .18em; text-transform: uppercase; color: var(--fg-3); flex: none; }
.tb-fiche__v { font-size: 14.5px; line-height: 1.5; color: var(--fg); text-align: right; }
.tb-fiche__r { transition: border-color .3s var(--sortie); }
.tb-fiche__r:hover { border-bottom-color: var(--mark); }

/* ------------------------------- le contact -------------------------- */

.tb-contact .tb-h2 { max-width: 14ch; }
.tb-mail {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(19px, 1.8vw, 24px);
  color: var(--fg);
  border-bottom: 1px solid transparent;
  transition: border-color .3s var(--sortie);
}
.tb-mail:hover { border-bottom-color: var(--mark); }
.tb-bloc-mail {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px 0;
  border-block: 1px solid var(--hair);
}

/* ------------------------------ le pied de page ---------------------- */

.tb-pied { background: var(--ink); color: var(--sur-ink); }
.tb-pied__g {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) repeat(3, minmax(0, 1fr));
  gap: clamp(30px, 3.6vw, 56px);
  padding-bottom: clamp(34px, 3.6vw, 52px);
}
.tb-pied__col { display: flex; flex-direction: column; gap: 9px; font-size: 14px; color: var(--sur-ink-2); }
.tb-pied__col a { transition: color .25s var(--sortie); }
.tb-pied__col a:hover { color: var(--taupe-clair); }
.tb-pied__bas {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px 26px;
  padding-top: 22px;
  border-top: 1px solid #43444A;
  font-size: 12.5px;
  color: var(--sur-ink-2);
}
.tb-pied__bas a:hover { color: var(--taupe-clair); }

/* --------------------------- la barre d'appel ------------------------ */

.tb-barre {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 18px calc(11px + env(safe-area-inset-bottom));
  background: var(--ink);
  color: var(--sur-ink);
  transform: translateY(102%);
  transition: transform .5s var(--sortie);
}
.tb-barre--on { transform: none; }
.tb-barre__lbl { font-size: 10px; font-weight: 500; letter-spacing: .2em; text-transform: uppercase; color: var(--taupe-clair); }
.tb-barre__n { font-size: 18px; font-weight: 500; }
.tb-barre__b {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  padding: 11px 20px;
  background: var(--paper);
  color: var(--ink);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* -------------------------------- mouvement -------------------------- */

[data-lever] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .85s var(--sortie), transform .95s var(--sortie);
}
[data-lever].tb-vu { opacity: 1; transform: none; }

/* -------------------------------- adaptations ------------------------ */

@media (max-width: 1080px) {
  .tb-pied__g { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Le menu tombe avant la trame. Sept entrées, le numéro et le bouton
   demandent 1 160 px ; en dessous, l'en-tête ne garde que la marque,
   le numéro et le devis — et l'appel reste à portée. Mesuré, pas
   estimé : à 1 130 px la marge tombait à zéro et la barre passait à la
   ligne sans que rien d'autre ne le signale. */
@media (max-width: 1160px) {
  .tb-nav { display: none; }
}

@media (max-width: 900px) {
  /* .tb-hero .tb-duo pose son propre ratio avec une spécificité de 2 :
     il doit être répété ici, sinon la règle de base ne le rattrape jamais
     et le héros reste sur deux colonnes jusqu'à 320 px. */
  .tb-duo, .tb-duo--r, .tb-hero__duo { grid-template-columns: minmax(0, 1fr); }
  /* Sur une colonne, la photo n'a plus de hauteur à épouser. Elle garde
     un cadrage DEBOUT : la porte est un sujet vertical, et un format
     paysage la coupe en deux. 4/5 la montre presque entière sans
     manger tout l'écran. */
  .tb-hero__f { min-height: 0; }
  .tb-hero__i { position: static; aspect-ratio: 4 / 5; height: auto; object-position: center 72%; }
  .tb-promesses { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: clamp(26px, 3vw, 38px); }
  .tb-promesse:nth-child(3)::before { content: none; }
  .tb-promesse:nth-child(odd) { padding-left: 0; }
  .tb-promesse:nth-child(even) { padding-right: 0; }
  .tb-colle, .tb-portrait { position: static; }
  .tb-portrait { max-width: 380px; }
  .tb-tete .tb-note { text-align: left; }
  .tb-hero .tb-h1 { max-width: 17ch; }
}

@media (max-width: 860px) {
  .tb-barre { display: flex; }
  .tb-pied { padding-bottom: 74px; }
}

@media (max-width: 620px) {
  .tb-portrait { max-width: 300px; }
  .tb-hdr__bar { min-height: 70px; }
  /* Sur téléphone, l'en-tête ne garde que la marque : le numéro et le
     bouton y passaient à la ligne, et l'appel est de toute façon porté
     par le héros puis par la barre fixe, jamais à plus d'un pouce. */
  .tb-hdr__tel, .tb-hdr .tb-appel--menu { display: none; }
  /* Quinze cartes pleines empilées font un défilement interminable :
     les travaux se resserrent en index de deux colonnes, icône et
     intitulé seulement, qui se suffisent. Les cinq interlocuteurs, eux,
     gardent leur description : ils ne sont que cinq et c'est elle qui
     porte la valeur. La carte ouverte garde la sienne aussi, c'est un
     appel à décrire autre chose. */
  .tb-cartes:not(.tb-cartes--cinq) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    grid-auto-rows: auto;
  }
  .tb-cartes:not(.tb-cartes--cinq) .tb-carte {
    display: flex;
    grid-row: auto;
    gap: 10px;
    padding: 17px 15px 18px;
  }
  .tb-cartes:not(.tb-cartes--cinq) .tb-carte__d,
  .tb-cartes:not(.tb-cartes--cinq) .tb-lien { display: none; }
  .tb-cartes:not(.tb-cartes--cinq) .tb-carte__t { font-size: 15px; }
  .tb-cartes:not(.tb-cartes--cinq) .tb-icone { width: 24px; height: 24px; }

  .tb-carte--large { grid-column: 1 / -1; }
  .tb-carte--large .tb-carte__d { display: block; }
  .tb-carte--large .tb-lien { display: inline-flex; }
  /* Sur une colonne, l'alignement entre colonnes n'a plus d'objet : le
     subgrid ne ferait que réserver la hauteur du chiffre sous chaque
     coche. */
  /* Sur une colonne, l'alignement entre colonnes n'a plus d'objet et le
     subgrid ne ferait que réserver la hauteur de la plus haute accroche
     sous chaque icône. Le filet de séparation se couche. */
  .tb-promesses { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; row-gap: 0; }
  .tb-promesse { display: flex; grid-row: auto; padding: 26px 0; }
  .tb-promesse::before {
    left: 0; right: 0; top: 0; bottom: auto;
    width: auto; height: 1px;
    transform: scaleX(0); transform-origin: left;
  }
  [data-lever].tb-vu .tb-promesse::before { transform: scaleX(1); }
  .tb-promesse:first-child { padding-top: 4px; }
  .tb-fiche__r { flex-direction: column; gap: 3px; }
  .tb-fiche__v { text-align: left; }
  .tb-bloc-mail { flex-direction: column; align-items: flex-start; }
}

@media (pointer: coarse) {
  .tb-vue__b:hover img { transform: none; }
  .tb-nav a, .tb-pied__col a, .tb-pied__bas a, .tb-renvoi,
  .tb-marque, .tb-tel, .tb-mail, .tb-hdr__tel {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  .tb-q__r { padding-block: 21px; }
  .tb-bouton { padding-block: 14px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  [data-lever] { opacity: 1; transform: none; }
  .tb-ouvre > *, .tb-visite { opacity: 1; transform: none; }
  [data-lever] .tb-carte, [data-lever] .tb-avis__i { opacity: 1; translate: none; }
  .tb-fil::after { transform: scaleX(1); }

  /* Le titre du héros, la marque et le volet « après » sont posés
     d'emblée : sans ça, un cache resterait fermé sur un contenu. */
  .tb-lignes > span > span { transform: none; animation: none; }
  .tb-lignes .tb-vx { color: var(--mark-v); animation: none; }
  .tb-marque--anime .tb-marque__m,
  .tb-marque--anime .tb-marque__t { clip-path: none; animation: none; }
  /* Les animations liées au défilement ne se règlent pas par la durée :
     sur une chronologie de défilement, une durée écrasée à 0,01 ms les
     enverrait à leur état final. On coupe la chronologie elle-même. */
  .tb-hdr, .tb-hdr__jauge { animation-timeline: none; animation-name: none; }
}
