/* ORI by Juraweal — Main CSS */

/* ─── Typographie ────────────────────────────────────────────────────────────
   Le client impose Univers Condensed (Laurent, 13/08/2026), police sous licence
   payante Linotype/Monotype dont la licence webfont est facturee au volume de
   pages vues. En attendant un arbitrage budgetaire, on utilise Roboto Condensed,
   substitut libre le plus proche disponible sur Google Fonts.

   Pour basculer sur Univers Condensed le jour ou la licence est acquise :
   remplacer la valeur des deux variables ci-dessous et le <link> Google Fonts
   dans templates/base.html.twig. Aucun autre fichier a toucher.
   ------------------------------------------------------------------------- */
:root {
  --font-display: 'Roboto Condensed', 'Arial Narrow', 'Helvetica Neue Condensed', system-ui, sans-serif;
  --font-body: 'Roboto Condensed', 'Arial Narrow', 'Helvetica Neue Condensed', system-ui, sans-serif;

  /* Hauteur de la barre de navigation, qui est en position fixe. Tout contenu
     place en tete de page doit s'en degager, sinon il passe dessous. Les hero
     s'en sortent grace a leur grand padding, mais un bandeau colle en haut,
     non. */
  --topbar-h: 82px;
}

/* Animations */
@keyframes dpfloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-18px); }
}

@keyframes dpfloat2 {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-11px); }
}

@keyframes dppulse {
  0%, 100% { opacity: .45; transform: translate(-50%, -50%) scale(1); }
  50% { opacity: .8; transform: translate(-50%, -50%) scale(1.08); }
}

@keyframes dpspin {
  to { transform: rotate(360deg); }
}

@keyframes dpspinr {
  to { transform: rotate(-360deg); }
}

@keyframes dpshimmer {
  0% { transform: translateX(-130%) skewX(-18deg); }
  100% { transform: translateX(130%) skewX(-18deg); }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

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

body {
  margin: 0;
  padding: 0;
}

/* Scroll reveal initial state (set by JS) */
[data-reveal] {
  /* Elements start visible; JS will hide them and animate in */
}


/* -------------------------------------------------------------------------
   Pyramide ORI-Locus, en volume
   Quatre faces triangulaires assemblees en 3D, aux proportions de Khephren :
   6 de haut pour 9,66 de large.

   La geometrie en decoule et n'est pas a ajuster a l'oeil. Pour une base de
   9,66 et une hauteur de 6, la demi-base vaut 4,83 et l'apotheme de chaque face
   sqrt(6² + 4,83²) = 7,7025, soit 0,79736 fois la base. Chaque face s'incline
   de asin(4,83 / 7,7025) = 38,834° depuis la verticale, ce qui ramene les
   quatre sommets exactement sur l'axe, a la hauteur voulue.

   Changer une de ces valeurs sans recalculer les autres ouvre la pyramide ou la
   referme : les faces ne se rejoignent plus au sommet.
   ------------------------------------------------------------------------- */
.ori-pyramide {
  --base: 240px;
  --apotheme: calc(var(--base) * 0.79736);
  --inclinaison: 38.834deg;
  /* Hauteur reellement occupee : apotheme x cos(38,834°). */
  --hauteur: calc(var(--base) * 0.62112);

  /* Prise de vue. Ces deux valeurs ne sont pas decoratives : elles decident si
     le carre de la base se lit comme un carre.

     La distance a d'abord valu 3,4 fois la base, soit un teleobjectif : la
     perspective existait mais ne convergeait presque pas. L'inclinaison valait
     14°, ce qui revient a regarder le plan de la base en rasant, ou un carre se
     comprime en un ruban. Mesure a l'appui, les cotes opposes de la base
     tombaient alors a 1,2° du parallelisme sur toute la plage du trois-quarts.
     L'oeil y voyait un parallelogramme, et il avait raison.

     A 2,2 fois la base et 20°, le pire cas de la meme plage remonte a 8,1°.

     Ne pas monter l'inclinaison plus haut en croyant mieux faire : au-dela de
     25° la base occupe plus de surface projetee que les faces, et la silhouette
     cesse de se lire comme une pyramide pour devenir une gemme trapue. */
  --distance: 2.2;
  --prise-de-vue: 20deg;

  width: var(--base);
  height: var(--hauteur);
  perspective: calc(var(--base) * var(--distance));
  perspective-origin: 50% 50%;
  filter: drop-shadow(0 26px 36px rgba(232, 200, 122, .20));
}

.ori-pyramide--flotte { animation: dpfloat 7s ease-in-out infinite; }

/* Miniatures de panier : meme volume, mais immobile et sans halo. A cette
   taille une rotation devient un scintillement, et le halo bave sur le texte. */
.ori-pyramide--fixe { filter: none; }
.ori-pyramide--fixe .ori-pyramide__corps { animation: none; transform: rotateX(var(--prise-de-vue)) rotateY(38deg); }
.ori-pyramide--fixe .ori-pyramide__halo,
.ori-pyramide--fixe .ori-pyramide__ombre { display: none; }

.ori-pyramide__corps {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transform: rotateX(var(--prise-de-vue)) rotateY(45deg);
  animation: oriTourne 22s ease-in-out infinite;
}

.ori-pyramide__face {
  position: absolute;
  left: 0;
  bottom: 0;
  width: var(--base);
  height: var(--apotheme);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  transform-origin: 50% 100%;
  /* L'ordre compte. On oriente la face autour de l'axe, on l'ecarte d'une
     demi-base, puis on couche son sommet vers l'axe. L'inclinaison est
     positive : en CSS, rotateX positif bascule le haut vers l'arriere, donc
     vers l'interieur une fois la face poussee vers l'avant. Au signe oppose la
     pyramide s'ouvre en corolle au lieu de se refermer. */
  transform:
    rotateY(calc(var(--i) * 90deg))
    translateZ(calc(var(--base) / 2))
    rotateX(var(--inclinaison));
  /* Les faces arriere restent visibles : le quartz coule dans le PMMA est
     translucide, les masquer creuserait la pyramide. */
  backface-visibility: visible;
}

/* Quartz dans du PMMA : translucide, plus clair au sommet, dore vers la base.
   Chaque face porte sa propre intensite pour que le volume se lise a l'arret. */
.ori-pyramide__face { background: linear-gradient(180deg, rgba(196,238,255,.60), rgba(126,150,255,.22) 46%, rgba(232,200,122,.34)); }
.ori-pyramide__face[style*="--i:1"] { filter: brightness(.82); }
.ori-pyramide__face[style*="--i:2"] { filter: brightness(.68); }
.ori-pyramide__face[style*="--i:3"] { filter: brightness(.82); }

/* La base. Elle n'existait pas : le bas de la pyramide n'etait qu'un assemblage
   de quatre aretes, et l'oeil devait deviner un plan a partir de rien. Le
   materiau etant translucide, cette face se voit a travers les autres, comme
   sur la photo du produit.

   rotateX(90deg) couche le carre, puis translateY le recentre : apres la
   rotation, l'axe Y local pointe vers Z, une demi-base ramene donc le carre de
   -base..0 a -base/2..+base/2. */
.ori-pyramide__base {
  position: absolute;
  left: 0;
  bottom: 0;
  width: var(--base);
  height: var(--base);
  transform-origin: 50% 100%;
  transform: rotateX(90deg) translateY(calc(var(--base) / 2));
  /* Assourdie a dessein. On regarde la pyramide d'au-dessus : cette face est le
     sol vu au travers du quartz, pas une plaque posee devant. Rendue au meme
     eclat que les faces, elle prenait le dessus et la silhouette se lisait
     comme une gemme a huit faces. */
  background: linear-gradient(160deg, rgba(126, 150, 255, .13), rgba(232, 200, 122, .10));
  /* L'arete est ce qui fait lire un objet translucide : sans elle le carre se
     devine, avec elle il se voit. C'est aussi ce que montre la photo du
     produit, ou le PMMA accroche la lumiere sur chaque arete. */
  border: 1px solid rgba(255, 255, 255, .22);
}

.ori-pyramide__halo {
  position: absolute;
  top: 46%;
  left: 50%;
  width: calc(var(--base) * 1.35);
  height: calc(var(--base) * 1.35);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, var(--accent-soft, rgba(232,200,122,.18)) 0%, transparent 62%);
  filter: blur(20px);
  animation: dppulse 6s ease-in-out infinite;
  pointer-events: none;
}

.ori-pyramide__ombre {
  position: absolute;
  left: 50%;
  bottom: -6px;
  width: calc(var(--base) * 0.66);
  height: 18px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse, var(--accent-soft, rgba(232,200,122,.30)), transparent 70%);
  filter: blur(7px);
  pointer-events: none;
}

/* Le tour complet est abandonne. Une pyramide vue de face est un triangle plat,
   et vue de trois quarts exactement a 45° ses deux faces visibles sont
   identiques : le tour traversait quatre fois chacun de ces angles morts, ou le
   volume cesse de se lire. On oscille donc dans la plage ou il se lit toujours,
   comme un presentoir qui se balance plutot qu'un plateau qui tourne. */
@keyframes oriTourne {
  0%, 100% { transform: rotateX(var(--prise-de-vue)) rotateY(28deg); }
  50%      { transform: rotateX(var(--prise-de-vue)) rotateY(62deg); }
}

/* Une rotation permanente gene une partie des visiteurs, et le systeme le dit :
   on s'arrete sur une vue de trois quarts, qui montre le volume sans bouger. */
@media (prefers-reduced-motion: reduce) {
  .ori-pyramide__corps { animation: none; transform: rotateX(var(--prise-de-vue)) rotateY(45deg); }
  .ori-pyramide--flotte,
  .ori-pyramide__halo { animation: none; }
}

/* -------------------------------------------------------------------------
   Complementarite des deux pieces

   Le lieu a gauche, la personne a droite, reliees par un axe. Pas une balance :
   une balance pese deux choses l'une contre l'autre, c'est la figure de
   l'opposition et de l'equivalence, quand le propos est justement qu'elles ne
   s'opposent pas et ne se remplacent pas. Elle evoque en outre la justice, et
   met sur un meme flechau deux objets de prix et de fonction differents.

   Sous 700px les deux domaines s'empilent et l'axe bascule a la verticale : il
   continue de relier, dans le sens de lecture.
   ------------------------------------------------------------------------- */
.ori-complement {
  display: grid;
  grid-template-columns: 1fr;
  gap: 22px;
  align-items: center;
  justify-items: center;
  max-width: 620px;
  margin: 0 auto;
}

.ori-complement__axe {
  width: 1px;
  height: 46px;
  background: linear-gradient(180deg, transparent, var(--accent-soft, rgba(232, 200, 122, .5)), transparent);
  position: relative;
}

/* Le point sur l'axe dit que le lien passe par la, sans fleche ni sens impose :
   le pendentif se pose sur la pyramide, la pyramide n'a besoin de rien. */
.ori-complement__axe::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: var(--accent, #e8c87a);
  box-shadow: 0 0 10px var(--accent, #e8c87a);
}

.ori-complement__pole {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
}

@media (min-width: 700px) {
  .ori-complement { grid-template-columns: 1fr auto 1fr; gap: 30px; }
  .ori-complement__axe { width: 120px; height: 1px; background: linear-gradient(90deg, transparent, var(--accent-soft, rgba(232, 200, 122, .5)), transparent); }
}

/* -------------------------------------------------------------------------
   Carousel produit
   Fondu enchaine entre 3 photos, autoplay toutes les 4 secondes. La premiere
   image est dans le flux (position relative) pour donner sa hauteur au
   conteneur ; les suivantes sont absolues et se superposent.
   ------------------------------------------------------------------------- */
.dp-carousel {
  position: relative;
  overflow: hidden;
}

.dp-carousel__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1s ease;
}

.dp-carousel__slide img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* La premiere image reste dans le flux pour dimensionner le conteneur. */
.dp-carousel__slide:first-child {
  position: relative;
}

.dp-carousel__slide--active {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .dp-carousel__slide { transition: none; }
}

/* -------------------------------------------------------------------------
   Carousel de témoignages
   Trois cartes visibles à la fois, défilement par page avec animation de
   slide. Boucle infinie par clonage des trois premières et trois dernières
   cartes aux extrémités de la piste.
   --n (posé en style inline) = nombre total de cartes dans le DOM (clones
   compris). La piste fait --n/3 fois la largeur de la fenêtre, et chaque
   carte en occupe 1/--n. La position initiale saute les 3 premiers clones.
   ------------------------------------------------------------------------- */
.dp-temoignages {
  display: flex;
  align-items: center;
  gap: 12px;
}

.dp-temoignages__fenetre {
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

.dp-temoignages__piste {
  display: flex;
  /* Nombre de cartes visibles a la fois. Trois etaient imposees a toutes les
     largeurs : dans une fenetre de 254px sur telephone, chaque carte tombait
     a 85px et le temoignage devenait illisible. Le JavaScript lit cette meme
     variable pour avancer d'une page, sans quoi la fleche sauterait trois
     avis alors qu'un seul est montre. */
  --par-page: 1;
  width: calc(var(--n) / var(--par-page) * 100%);
  transform: translateX(calc(-3 / var(--n) * 100%));
  transition: transform 0.5s ease;
}

/* Palier intermediaire : a 820px de large, une seule carte occupait 644px et
   la ligne de texte devenait trop longue pour se lire confortablement. */
@media (min-width: 700px) {
  .dp-temoignages__piste { --par-page: 2; }
}

@media (min-width: 900px) {
  .dp-temoignages__piste { --par-page: 3; }
}

.dp-temoignages__carte {
  flex: 0 0 calc(100% / var(--n));
  min-width: 0;
  padding: 0 8px;
  box-sizing: border-box;
}

.dp-temoignages__contenu {
  background: rgba(255, 255, 255, .035);
  border: 1px dashed rgba(232, 200, 122, .22);
  border-radius: 18px;
  padding: 26px;
  text-align: center;
}

.dp-temoignages__fleche {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid rgba(232, 200, 122, .28);
  background: rgba(255, 255, 255, .04);
  color: var(--accent, #e8c87a);
  cursor: pointer;
  transition: background .2s, border-color .2s;
}

.dp-temoignages__fleche:hover {
  background: rgba(232, 200, 122, .12);
  border-color: var(--accent, #e8c87a);
}

@media (prefers-reduced-motion: reduce) {
  .dp-temoignages__piste { transition: none; }
}

@media (max-width: 540px) {
  .dp-temoignages__contenu { padding: 16px 10px; font-size: 12px; }
}

/* -------------------------------------------------------------------------
   Barre de navigation
   Elle ne se replie pas : sur un ecran etroit le bouton de precommande sortait
   de l'ecran, coupe net. Il disparait donc en dessous de 620px — la page porte
   de toute facon son propre appel a l'action, et une barre qui deborde coute
   plus cher qu'un bouton en moins.
   ------------------------------------------------------------------------- */
.ori-topbar { max-width: 100vw; overflow-x: clip; }

/* Le sigle « ORI » n'occupe qu'un quart de la hauteur du logo : en dessous de
   50px il devient illisible. Mais a 52px sur un telephone, le diamant vient
   toucher le selecteur de langue. Compromis par palier. */
.ori-topbar__logo { height: 52px; }

@media (max-width: 520px) {
  .ori-topbar__logo { height: 42px; }
}

@media (max-width: 620px) {
  .ori-topbar__cta { display: none !important; }
}


/* Le marqueur natif de <details> reste affiche sur Safari malgre
   list-style:none : seul ce pseudo-element le retire. */
#dp-checkout-overlay summary::-webkit-details-marker { display: none; }

/* ─── Hero : deux colonnes a partir de 900px ─────────────────────────────
   Le hero doit tenir dans un ecran (cf. CLAUDE.md, « Hauteur du hero »).
   Empiler la photo sous le texte ajouterait plus de 300px et ferait passer
   le bouton et le bandeau de perimetre sous la ligne de flottaison.

   En dessous de 900px la photo ne prend donc aucune place dans le flux :
   elle passe en fond, derriere le texte, fortement assombrie. Au-dessus,
   elle devient la colonne de droite et le texte s'aligne a gauche.

   Ces regles vivent ici et non en style en ligne : une requete de media ne
   s'ecrit pas dans un attribut style. */
.ori-hero__grille {
  display: grid;
  grid-template-columns: 1fr;
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  align-items: center;
}

.ori-hero__texte,
/* Le bandeau de perimetre vit hors de la grille, et n'etait donc pas remonte
   au-dessus du calque image. En dessous de 900px ce calque est absolu avec
   z-index 0 : il se peint par-dessus tout element non positionne du meme
   parent, voile radial compris, qui est opaque en bas de section.
   Le bandeau restait lisible pendant l'animation d'apparition, qui pose un
   transform et cree donc un contexte d'empilement, puis disparaissait sous le
   voile des que le transform revenait a none. D'ou l'effet « il s'efface au
   bout de deux secondes ». */
.ori-hero__perimetre { position: relative; z-index: 1; }

.ori-hero__visuel {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.ori-hero__visuel img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Un bandeau visible dans le flux n'est pas possible : il reste 116px de
     marge sur un ecran de 844px, mais zero sur un 375x667. L'image passe donc
     en fond a toutes les tailles sous 900px, et son opacite est le seul
     reglage disponible. */
  opacity: .34;
}

/* Voile de lisibilite. Sans lui, le texte clair passe sur les zones claires
   du quartz et devient illisible par endroits, ce qui ne se voit qu'a
   certaines largeurs. */
.ori-hero__visuel::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 42%,
    rgba(12, 10, 36, .62) 0%, rgba(12, 10, 36, .90) 55%, #0c0a24 100%);
}

@media (min-width: 900px) {
  .ori-hero__grille {
    grid-template-columns: minmax(0, 1fr) minmax(0, .92fr);
    gap: clamp(30px, 4vw, 60px);
  }

  .ori-hero__visuel {
    position: relative;
    inset: auto;
    aspect-ratio: 4 / 3;
    border-radius: 20px;
    border: 1px solid rgba(255, 255, 255, .10);
    box-shadow: 0 34px 90px -34px rgba(0, 0, 0, .85);
  }

  .ori-hero__visuel img { opacity: 1; }

  /* Le voile devient un simple fondu vers le texte, cote gauche. */
  .ori-hero__visuel::after {
    background: linear-gradient(90deg,
      rgba(12, 10, 36, .55) 0%, rgba(12, 10, 36, 0) 36%);
  }

  .ori-hero__texte { text-align: left; }
  .ori-hero__texte h1 { max-width: 20ch; }
  .ori-hero__texte .ori-hero__sous { margin-left: 0; margin-right: 0; max-width: 46ch; }
  .ori-hero__texte .ori-hero__boutons,
  .ori-hero__texte .ori-hero__reassure { justify-content: flex-start; }
}
