/* ==========================================================================
   QUALITECH : base
   Reset, fondations typographiques, mise en page, primitives partagées.
   Dépend de tokens.css. Aucune valeur brute : tout passe par les variables.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6.5rem; /* compense l'en-tête collant lors des ancres */
}

body {
  font-family: var(--qt-police);
  font-size: var(--qt-t-corps);
  font-weight: var(--qt-w-normal);
  line-height: var(--qt-lh-corps);
  letter-spacing: var(--qt-ls-corps);
  color: var(--qt-encre);
  background-color: var(--qt-fond);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-synthesis-weight: none;
  min-height: 100dvh;
  overflow-x: hidden;
}

img, svg, video, canvas, picture {
  display: block;
  max-width: 100%;
  height: auto;
}

img { background-color: var(--qt-fond-alt); } /* évite le flash blanc au chargement */

input, button, textarea, select {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

button { background: none; border: 0; cursor: pointer; }

ul, ol { list-style: none; padding: 0; }

a { color: inherit; text-decoration: none; }

table { border-collapse: collapse; width: 100%; }

:where(h1, h2, h3, h4, h5, h6) {
  font-weight: var(--qt-w-gras);
  line-height: var(--qt-lh-titre);
  letter-spacing: var(--qt-ls-titre);
  text-wrap: balance;
}

p { text-wrap: pretty; }


/* --------------------------------------------------------------------------
   Focus : visible partout, anneau doré. Jamais supprimé.
   -------------------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--qt-focus);
  outline-offset: 3px;
  border-radius: var(--qt-rayon-xs);
}

:focus:not(:focus-visible) { outline: none; }


/* --------------------------------------------------------------------------
   Accessibilité
   -------------------------------------------------------------------------- */

.qt-invisible {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.qt-evitement {
  position: absolute;
  top: var(--qt-e-s);
  left: var(--qt-e-s);
  z-index: var(--qt-z-panneau);
  padding: var(--qt-e-xs) var(--qt-e-m);
  background-color: var(--qt-marine);
  color: var(--qt-blanc);
  border-radius: var(--qt-rayon-s);
  font-weight: var(--qt-w-semi);
  transform: translateY(-160%);
  transition: transform var(--qt-d-micro) var(--qt-ease);
}

.qt-evitement:focus { transform: translateY(0); }


/* --------------------------------------------------------------------------
   Mise en page
   -------------------------------------------------------------------------- */

.qt-conteneur {
  width: 100%;
  max-width: var(--qt-largeur-max);
  margin-inline: auto;
  padding-inline: var(--qt-gouttiere);
}

.qt-conteneur--etroit { max-width: var(--qt-largeur-etroite); }

.qt-section {
  position: relative;
  padding-block: var(--qt-section-y);
  background-color: var(--qt-fond);
  color: var(--qt-encre);
}

.qt-section--large { padding-block: var(--qt-section-y-large); }
.qt-section--serree { padding-block: var(--qt-e-2xl); }
.qt-section--alt { background-color: var(--qt-fond-alt); }

/* Grille de 12 colonnes. Les blocs prennent des fractions asymétriques. */
.qt-grille {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--qt-grille-ecart);
}

/* Le split éditorial par défaut du site : jamais 50/50.

   Alignement en HAUT. Il était en bas, pour qu'un titre et un chapeau de
   hauteurs voisines posent sur la même ligne. L'intention tenait tant que
   les deux colonnes se ressemblaient ; elle devenait catastrophique dès
   qu'elles divergeaient.

   Mesuré sur le site : le bloc FAQ met un en-tête de 148 px en face d'un
   accordéon de 685 px. Calé en bas, l'en-tête tombait 537 px plus bas que
   le haut de la grille, laissant un trou de la hauteur d'un écran. Ce bloc
   existe sur huit pages.

   En haut, aucune configuration ne peut créer de trou : la colonne courte
   commence où commence la longue. Le léger décalage de dernière ligne que
   l'alignement bas corrigeait coûte quelques pixels ; le trou coûtait la
   moitié d'un écran. Un bloc qui veut autre chose le redemande. */
.qt-split {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  gap: var(--qt-grille-ecart);
  align-items: start;
}

.qt-split--inverse { grid-template-columns: 1fr 1.6fr; }

@media (max-width: 900px) {
  .qt-split,
  .qt-split--inverse { grid-template-columns: 1fr; }
}

.qt-pile {
  display: flex;
  flex-direction: column;
  gap: var(--qt-e-m);
  align-items: flex-start;
}

.qt-rang {
  display: flex;
  flex-wrap: wrap;
  gap: var(--qt-e-s);
  align-items: center;
}


/* --------------------------------------------------------------------------
   Typographie : classes contrat
   Un seul endroit décide de la taille d'un H2. Partout ailleurs on appelle
   la classe. C'est ce qui garantit la cohérence entre les 11 pages.
   -------------------------------------------------------------------------- */

.qt-display-xl {
  font-size: var(--qt-t-display-xl);
  /* Seul le H1 du hero est en 700. Ce display sert au H2 du CTA final,
     qui reste donc au niveau des autres titres de section. */
  font-weight: var(--qt-w-h2);
  line-height: 1.04;
  letter-spacing: -0.032em;
  text-wrap: balance;
}

.qt-display-l {
  font-size: var(--qt-t-display-l);
  font-weight: var(--qt-w-h2);
  /* Comme pour le H1 : plus c'est gras, plus il faut serrer. */
  line-height: 1.06;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.qt-titre {
  font-size: var(--qt-t-titre);
  font-weight: var(--qt-w-titre);
  line-height: var(--qt-lh-titre);
  letter-spacing: var(--qt-ls-titre);
}

.qt-sous-titre {
  font-size: var(--qt-t-sous-titre);
  font-weight: var(--qt-w-semi);
  line-height: var(--qt-lh-sous);
  letter-spacing: var(--qt-ls-sous);
}

.qt-chapeau {
  font-size: var(--qt-t-chapeau);
  font-weight: var(--qt-w-leger);
  line-height: var(--qt-lh-sous);
  letter-spacing: var(--qt-ls-sous);
  color: var(--qt-encre-douce);
  max-width: var(--qt-mesure);
}

.qt-corps { font-size: var(--qt-t-corps); }
.qt-petit { font-size: var(--qt-t-petit); }

.qt-mention {
  font-size: var(--qt-t-mention);
  line-height: var(--qt-lh-dense);
  color: var(--qt-encre-tenue);
}

/* Sur-titre : majuscules très espacées, précédé d'un filet doré */
.qt-sur-titre {
  display: inline-flex;
  align-items: center;
  gap: var(--qt-e-xs);
  font-size: var(--qt-t-sur-titre);
  font-weight: var(--qt-w-semi);
  letter-spacing: var(--qt-ls-sur-titre);
  text-transform: uppercase;
  color: var(--qt-accent-texte);
  line-height: 1;
}

.qt-sur-titre::before {
  content: "";
  inline-size: 1.75rem;
  block-size: 2px;
  background-color: var(--qt-accent);
  flex: none;
}

.qt-sur-titre--nu::before { display: none; }

/* Chiffres : toujours tabulaires, jamais en police à chasse fixe */
.qt-stat {
  font-size: var(--qt-t-stat);
  font-weight: var(--qt-w-normal);
  line-height: 1.0;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

.qt-chiffre {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

/* Accent éditorial : un mot par titre, pas plus.
   Pas de font-weight ni de letter-spacing ici : le mot hérite du poids et
   du tracking de son titre. L'italique et la couleur suffisent à le
   détacher — un poids plus léger le faisait décrocher de sa phrase.
   La police est chargée en ital,wght 200..800, l'italique gras est donc
   un vrai dessin et non un oblique synthétisé par le navigateur. */
/* Accent des titres. Plus d'italique : Outfit n'en livre pas, et un
   oblique synthétique se repère immédiatement. Le soulignement doré
   reprend le traitement que le hero appliquait déjà à ses propres accents,
   ce qui unifie au passage les deux au lieu d'en avoir deux différents. */
.qt-accentue {
  font-style: normal;
  position: relative;
  color: var(--qt-accent-texte);
}

.qt-accentue::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0.02em;
  block-size: 0.075em;
  background-color: var(--qt-or);
}

/* Dans un paragraphe, l'accent se distingue déjà par la couleur ; un filet
   au fil des lignes y ferait rature. */
.qt-chapeau .qt-accentue::after { content: none; }

.qt-mesure         { max-width: var(--qt-mesure); }
.qt-mesure--large  { max-width: var(--qt-mesure-large); }
.qt-mesure--etroite{ max-width: var(--qt-mesure-etroite); }


/* --------------------------------------------------------------------------
   Filets et séparateurs
   -------------------------------------------------------------------------- */

.qt-filet {
  border: 0;
  block-size: var(--qt-trait);
  background-image: linear-gradient(
    to right,
    transparent,
    var(--qt-filet-fort) 18%,
    var(--qt-filet-fort) 82%,
    transparent
  );
}

.qt-filet--plein { background: var(--qt-filet-clair); }


/* --------------------------------------------------------------------------
   Grain global
   Overlay SVG en turbulence. Non cliquable, au-dessus de tout.
   C'est ce qui empêche les grands aplats marine de faire plastique.
   -------------------------------------------------------------------------- */

.qt-grain {
  position: fixed;
  inset: 0;
  z-index: var(--qt-z-grain);
  pointer-events: none;
  opacity: var(--qt-grain-opacite);
  mix-blend-mode: overlay;
  background-repeat: repeat;
  background-size: 180px 180px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}


/* --------------------------------------------------------------------------
   Lueurs atmosphériques
   Halo doré très diffus, placé derrière le contenu des sections sombres.
   -------------------------------------------------------------------------- */

.qt-lueur {
  position: absolute;
  z-index: var(--qt-z-fond);
  pointer-events: none;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.55;
  background: radial-gradient(
    circle at center,
    color-mix(in srgb, var(--qt-or) 62%, transparent) 0%,
    transparent 70%
  );
}

.qt-lueur--marine {
  background: radial-gradient(
    circle at center,
    color-mix(in srgb, var(--qt-marine-voile) 78%, transparent) 0%,
    transparent 70%
  );
  opacity: 0.7;
}


/* --------------------------------------------------------------------------
   Reveals au scroll
   État initial posé en CSS pour éviter tout flash de contenu déjà animé.
   Le JS ne fait qu'ajouter .est-visible.
   -------------------------------------------------------------------------- */

.qt-reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity var(--qt-d-moyen) var(--qt-ease),
    transform var(--qt-d-moyen) var(--qt-ease);
  transition-delay: var(--qt-delai, 0ms);
  will-change: opacity, transform;
}

.qt-reveal.est-visible {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* Variante masque : l'image se dévoile par le bas, façon rideau.
   Réservée aux IMAGES. Le site n'avait qu'un seul geste de révélation pour
   375 éléments, ce qui se remarque au bout de deux pages. Le texte garde
   la translation, les photos prennent le rideau.

   ATTENTION : le clip-path est posé sur les ENFANTS, jamais sur l'élément
   observé. Un clip-path qui masque 100 % réduit l'aire visible à zéro, et
   l'IntersectionObserver ne rapporte alors JAMAIS d'intersection, même
   quand l'élément est à l'écran. On obtient un blocage circulaire : il
   faut le révéler pour qu'il soit vu, et qu'il soit vu pour le révéler.
   Vérifié au navigateur : isIntersecting reste false, ratio 0, alors que
   la boîte mesure 473 × 355. C'est pour ça que cette variante, écrite dès
   l'origine, n'avait jamais pu être utilisée. */
.qt-reveal-masque > * {
  clip-path: inset(0 0 100% 0);
  transform: scale(1.02);
  transition:
    clip-path var(--qt-d-long) var(--qt-ease-sortie),
    transform var(--qt-d-long) var(--qt-ease-sortie);
  transition-delay: var(--qt-delai, 0ms);
}

.qt-reveal-masque.est-visible > * {
  clip-path: inset(0 0 0 0);
  transform: none;
}

/* Sans JavaScript, tout reste visible */
.no-js .qt-reveal,
.no-js .qt-reveal-masque,
.no-js .qt-reveal-masque > * {
  opacity: 1;
  transform: none;
  clip-path: none;
}


/* --------------------------------------------------------------------------
   Respect des préférences système
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .qt-reveal,
  .qt-reveal-masque {
    opacity: 1;
    transform: none;
    clip-path: none;
  }
}

@media (prefers-contrast: more) {
  :root {
    --qt-encre-douce: var(--qt-marine);
    --qt-encre-tenue: var(--qt-marine);
    --qt-filet-clair: var(--qt-filet-fort);
  }
}


/* --------------------------------------------------------------------------
   Impression : le devis imprimé doit rester lisible
   -------------------------------------------------------------------------- */

@media print {
  .qt-grain,
  .qt-lueur,
  .qt-entete { display: none !important; }

  body { background: #fff; color: #000; }
  .qt-section { padding-block: 1.5rem; }
}
