/* =========================================================================
   KAIROS CORP — plan de travail bento
   Application d'une seule vue : la page ne défile pas sur grand écran.
   Cinq tuiles flottent dans une grille 12 × 8 et changent de place selon
   la section mise en avant. Chaque tuile est un conteneur : son contenu
   s'adapte à SA taille, pas à celle de la fenêtre.
   ========================================================================= */

/* ------------------------------------------------------------ Variables */
:root,
[data-theme="dark"] {
  --desk:       #08080A;
  --desk-2:     #0E0E11;
  --tile:       #141417;
  --tile-2:     #1A1A1E;
  --tile-line:  rgba(255, 255, 255, 0.065);
  --tile-lip:   rgba(255, 255, 255, 0.07);
  --bezel:      rgba(255, 255, 255, 0.05);

  --bento-ease: cubic-bezier(0.62, 0.02, 0.14, 1);
  --bento-dur:  720ms;
}

[data-theme="light"] {
  --desk:       #E7E4DE;
  --desk-2:     #DEDBD4;
  --tile:       #FAF9F7;
  --tile-2:     #FFFFFF;
  --tile-line:  rgba(12, 12, 14, 0.07);
  --tile-lip:   rgba(255, 255, 255, 0.9);
  --bezel:      rgba(12, 12, 14, 0.06);
}

/* ---------------------------------------------------------------- Socle */
html.app, body.app { height: 100%; }

body.app {
  background: var(--desk);
  overflow: hidden;                 /* pas de défilement sur grand écran */
  transition: background-color 0.5s var(--ease), color 0.5s var(--ease);
}

/* Le grain global reste, mais posé sur le plan de travail */
body.app::after { opacity: 0.045; }

.app-frame {
  --frame-gap: clamp(10px, 0.9vw, 16px);
  --rail-w: 66px;
  position: fixed;
  inset: clamp(10px, 1.1vw, 18px);
  /* Quatre zones, pas d'imbrication : le logo et la barre du haut sont des
     enfants directs, ce qui permet de les réordonner librement en mobile. */
  display: grid;
  --rail-col: var(--rail-w);
  --rail-open: clamp(178px, 13.5vw, 212px);
  grid-template-columns: var(--rail-col) minmax(0, 1fr);
  grid-template-rows: var(--rail-w) minmax(0, 1fr);
  transition: grid-template-columns var(--bento-dur) var(--bento-ease);
  grid-template-areas:
    "logo  top"
    "rail  stage";
  gap: var(--frame-gap);
  padding: var(--frame-gap);
  border-radius: clamp(20px, 1.9vw, 30px);
  background:
    radial-gradient(120% 90% at 15% 0%, var(--desk-2), transparent 60%),
    var(--desk);
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    inset 0 0 0 1px var(--bezel),
    0 34px 70px -34px rgba(0, 0, 0, 0.85);
}

/* Le rail ouvert repousse la scène au lieu de la recouvrir : rien n'est
   masqué. :has() permet de réagir sur le parent au survol de l'enfant. */
.app-frame:has(.rail:hover),
.app-frame:has(.rail :focus-visible),
.app-frame:has(.rail.is-open) { --rail-col: var(--rail-open); }

/* ------------------------------------------------------------ Logo ---- */
.rail__logo {
  grid-area: logo;
  width: var(--rail-w);
  height: var(--rail-w);
  justify-self: start;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--fg);
  background: var(--tile);
  border: 1px solid var(--tile-line);
  box-shadow: inset 0 1px 0 var(--tile-lip), 0 6px 16px -8px rgba(0, 0, 0, 0.7);
  transition: background-color 0.35s var(--ease), border-color 0.35s var(--ease);
}

.rail__logo:hover { background: var(--tile-2); border-color: var(--line-2); }
.rail__logo .k-mark { width: 27px; height: auto; }
.rail__logo rect { fill: currentColor; }

/* ------------------------------------------------------------ Rail ---- */
.rail {
  grid-area: rail;
  position: relative;
  z-index: 30;
}

/* Le panneau déborde par-dessus la scène quand il s'ouvre : la grille ne
   doit pas se recomposer juste parce qu'on survole le menu. */
.rail__panel {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--rail-w);
  display: flex;
  flex-direction: column;
  padding: 9px;
  border-radius: 100px;
  overflow: hidden;
  background: var(--tile);
  border: 1px solid var(--tile-line);
  box-shadow: inset 0 1px 0 var(--tile-lip), 0 10px 26px -16px rgba(0, 0, 0, 0.8);
  transition: width var(--bento-dur) var(--bento-ease),
              border-radius var(--bento-dur) var(--bento-ease),
              box-shadow 0.45s var(--ease),
              background-color 0.35s var(--ease);
}

.rail:hover .rail__panel,
.rail:has(:focus-visible) .rail__panel,
.rail.is-open .rail__panel {
  width: var(--rail-open);
  border-radius: 26px;
  background: var(--tile-2);
  box-shadow: inset 0 1px 0 var(--tile-lip), 0 22px 50px -18px rgba(0, 0, 0, 0.75);
}

.rail__nav {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: auto;
  margin-bottom: auto;
}

.navi {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.85rem;
  width: 100%;
  height: 46px;
  padding: 0 13px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--fg-2);
  cursor: pointer;
  white-space: nowrap;
  text-align: left;
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease),
              box-shadow 0.4s var(--ease);
}

.navi:hover { color: var(--fg); background: var(--surface-2); }

.navi__ico { width: 20px; height: 20px; flex: none; display: grid; place-items: center; }
.navi__ico svg { width: 18px; height: 18px; }

.navi__label {
  font-size: 0.87rem;
  letter-spacing: -0.01em;
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity 0.35s var(--ease), transform 0.45s var(--bento-ease);
}

.rail:hover .navi__label,
.rail:has(:focus-visible) .navi__label,
.rail.is-open .navi__label { opacity: 1; transform: none; }

/* L'élément actif : pastille pleine, comme dans la référence */
.navi[aria-current="true"] {
  background: var(--fg);
  color: var(--bg);
  box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.6);
}

.navi[aria-current="true"]:hover { background: var(--fg); color: var(--bg); }

.rail__foot { display: grid; gap: 6px; }

.rail__dot {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid var(--tile-line);
  background: var(--surface);
  color: var(--fg-2);
  cursor: pointer;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}

.rail__dot:hover { color: var(--fg); border-color: var(--line-2); }
.rail__dot svg { width: 17px; height: 17px; }

/* --------------------------------------------------------- Barre haute */
.topbar {
  grid-area: top;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  min-width: 0;
  padding-inline: clamp(4px, 0.6vw, 12px);
}

.topbar__id { display: flex; align-items: center; gap: 1rem; min-width: 0; }

.topbar__word {
  height: 13px;
  width: auto;
  flex: none;
  filter: invert(var(--logo-invert));
  transition: filter 0.5s var(--ease);
}

.topbar__sep { width: 1px; height: 18px; background: var(--line); flex: none; }

.topbar__where {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar__right { display: flex; align-items: center; gap: 0.75rem; flex: none; }

/* ---------------------------------------------------------------- Scène */
.stage {
  grid-area: stage;
  --cols: 12;
  --rows: 8;
  --gap: var(--frame-gap);
  --cw: calc((100% - (var(--cols) - 1) * var(--gap)) / var(--cols));
  --rh: calc((100% - (var(--rows) - 1) * var(--gap)) / var(--rows));
  position: relative;
  min-height: 0;
}

.tile {
  position: absolute;
  /* --cw ne contient de pourcentage que pour left/width, --rh que pour
     top/height : chacun se résout donc sur la bonne dimension. */
  left:   calc(var(--x) * (var(--cw) + var(--gap)));
  top:    calc(var(--y) * (var(--rh) + var(--gap)));
  width:  calc(var(--w) * var(--cw) + (var(--w) - 1) * var(--gap));
  height: calc(var(--h) * var(--rh) + (var(--h) - 1) * var(--gap));

  container-type: inline-size;
  container-name: tile;

  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: clamp(13px, 1.25vw, 22px);
  border-radius: clamp(14px, 1.3vw, 20px);
  background: var(--tile);
  border: 1px solid var(--tile-line);
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    0 2px 4px -2px rgba(0, 0, 0, 0.5),
    0 14px 30px -18px rgba(0, 0, 0, 0.75);
  overflow: hidden;
  /* Flottement : chaque tuile suit le pointeur d'une amplitude qui lui est
     propre (--par). --px / --py sont posés sur la scène par bento.js et
     hérités ici. Translation pure : aucune déformation, la tuile reste
     exactement dans sa case au repos. */
  transform: translate3d(calc(var(--px, 0) * var(--par, 0px)),
                         calc(var(--py, 0) * var(--par, 0px)), 0);

  transition:
    left   var(--bento-dur) var(--bento-ease),
    top    var(--bento-dur) var(--bento-ease),
    width  var(--bento-dur) var(--bento-ease),
    height var(--bento-dur) var(--bento-ease),
    transform 0.6s var(--ease),
    background-color 0.45s var(--ease),
    border-color 0.45s var(--ease),
    box-shadow 0.45s var(--ease);
}

.tile[data-open="true"] {
  background: var(--tile-2);
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    0 3px 6px -2px rgba(0, 0, 0, 0.5),
    0 30px 60px -28px rgba(0, 0, 0, 0.9);
}

/* Les tuiles en retrait se cliquent pour passer au premier plan */
.tile[data-open="false"] { cursor: pointer; }
.tile[data-open="false"]:hover { border-color: var(--line-2); }

/* Pendant le réarrangement, on atténue le contenu : les seuils de
   conteneur basculent en cours de route et le va-et-vient se verrait. */
.stage.is-morphing .tile__body { opacity: 0.35; }
.tile__body { transition: opacity 0.3s var(--ease); }

/* -------------------------------------------------- Composition par état */

/* Découpe au bord droit de la tuile hero : elle loge les pastilles de
   pagination des réalisations, à cheval sur la gouttière. Une découpe
   rogne aussi les ombres portées — elles passent en drop-shadow, qui
   épouse la silhouette réelle ; l'ombre intérieure du haut reste en
   box-shadow, elle tombe hors de la découpe.
   Les quatre angles sont congés sur cinq points chacun : un polygone ne
   courbe pas, mais à ce rayon la facettisation est invisible. */
.stage {
  --dn-w: 34px;    /* profondeur de la découpe */
  --dn-h: 120px;   /* hauteur                  */
  --dn-r: 14px;    /* rayon des congés         */

  /* Bulles de profil, entaillées dans le bord haut de la tuile hero.
     --b1..--b3 : distance du centre de chaque bulle au bord droit. Elles se
     déduisent d'un pas unique : le gap du flex vaut --bpas moins un diamètre,
     donc trois centres régulièrement espacés donnent trois écarts égaux sans
     qu'aucun nombre ne soit à recalculer à la main. */
  --bd: 46px;      /* diamètre de la bulle     */
  --ba: 7px;       /* anneau autour            */
  --bR: calc(var(--bd) / 2 + var(--ba));
  --bpas: 80px;    /* d'un centre au suivant   */
  --b1: 63px;
  --b2: calc(var(--b1) + var(--bpas));
  --b3: calc(var(--b1) + var(--bpas) * 2);

  /* Silhouette réelle de la tuile hero : deux entailles en haut pour les
     bulles, une encoche à droite pour les pastilles. Elle sert deux fois —
     au ::before qui dessine la carte, et au masque du filigrane, qui sans
     elle débordait dans l'encoche. Une seule source, pas de dérive. */
  --decoupe-hero: polygon(
    0 0,
    calc(100% - var(--b3) - var(--bR)) 0px,
    calc(100% - var(--b3) - var(--bR) * 0.9808) calc(var(--bR) * 0.1951),
    calc(100% - var(--b3) - var(--bR) * 0.9239) calc(var(--bR) * 0.3827),
    calc(100% - var(--b3) - var(--bR) * 0.8315) calc(var(--bR) * 0.5556),
    calc(100% - var(--b3) - var(--bR) * 0.7071) calc(var(--bR) * 0.7071),
    calc(100% - var(--b3) - var(--bR) * 0.5556) calc(var(--bR) * 0.8315),
    calc(100% - var(--b3) - var(--bR) * 0.3827) calc(var(--bR) * 0.9239),
    calc(100% - var(--b3) - var(--bR) * 0.1951) calc(var(--bR) * 0.9808),
    calc(100% - var(--b3)) var(--bR),
    calc(100% - var(--b3) + var(--bR) * 0.1951) calc(var(--bR) * 0.9808),
    calc(100% - var(--b3) + var(--bR) * 0.3827) calc(var(--bR) * 0.9239),
    calc(100% - var(--b3) + var(--bR) * 0.5556) calc(var(--bR) * 0.8315),
    calc(100% - var(--b3) + var(--bR) * 0.7071) calc(var(--bR) * 0.7071),
    calc(100% - var(--b3) + var(--bR) * 0.8315) calc(var(--bR) * 0.5556),
    calc(100% - var(--b3) + var(--bR) * 0.9239) calc(var(--bR) * 0.3827),
    calc(100% - var(--b3) + var(--bR) * 0.9808) calc(var(--bR) * 0.1951),
    calc(100% - var(--b3) + var(--bR)) 0px,
    calc(100% - var(--b2) - var(--bR)) 0px,
    calc(100% - var(--b2) - var(--bR) * 0.9808) calc(var(--bR) * 0.1951),
    calc(100% - var(--b2) - var(--bR) * 0.9239) calc(var(--bR) * 0.3827),
    calc(100% - var(--b2) - var(--bR) * 0.8315) calc(var(--bR) * 0.5556),
    calc(100% - var(--b2) - var(--bR) * 0.7071) calc(var(--bR) * 0.7071),
    calc(100% - var(--b2) - var(--bR) * 0.5556) calc(var(--bR) * 0.8315),
    calc(100% - var(--b2) - var(--bR) * 0.3827) calc(var(--bR) * 0.9239),
    calc(100% - var(--b2) - var(--bR) * 0.1951) calc(var(--bR) * 0.9808),
    calc(100% - var(--b2)) var(--bR),
    calc(100% - var(--b2) + var(--bR) * 0.1951) calc(var(--bR) * 0.9808),
    calc(100% - var(--b2) + var(--bR) * 0.3827) calc(var(--bR) * 0.9239),
    calc(100% - var(--b2) + var(--bR) * 0.5556) calc(var(--bR) * 0.8315),
    calc(100% - var(--b2) + var(--bR) * 0.7071) calc(var(--bR) * 0.7071),
    calc(100% - var(--b2) + var(--bR) * 0.8315) calc(var(--bR) * 0.5556),
    calc(100% - var(--b2) + var(--bR) * 0.9239) calc(var(--bR) * 0.3827),
    calc(100% - var(--b2) + var(--bR) * 0.9808) calc(var(--bR) * 0.1951),
    calc(100% - var(--b2) + var(--bR)) 0px,
    calc(100% - var(--b1) - var(--bR)) 0px,
    calc(100% - var(--b1) - var(--bR) * 0.9808) calc(var(--bR) * 0.1951),
    calc(100% - var(--b1) - var(--bR) * 0.9239) calc(var(--bR) * 0.3827),
    calc(100% - var(--b1) - var(--bR) * 0.8315) calc(var(--bR) * 0.5556),
    calc(100% - var(--b1) - var(--bR) * 0.7071) calc(var(--bR) * 0.7071),
    calc(100% - var(--b1) - var(--bR) * 0.5556) calc(var(--bR) * 0.8315),
    calc(100% - var(--b1) - var(--bR) * 0.3827) calc(var(--bR) * 0.9239),
    calc(100% - var(--b1) - var(--bR) * 0.1951) calc(var(--bR) * 0.9808),
    calc(100% - var(--b1)) var(--bR),
    calc(100% - var(--b1) + var(--bR) * 0.1951) calc(var(--bR) * 0.9808),
    calc(100% - var(--b1) + var(--bR) * 0.3827) calc(var(--bR) * 0.9239),
    calc(100% - var(--b1) + var(--bR) * 0.5556) calc(var(--bR) * 0.8315),
    calc(100% - var(--b1) + var(--bR) * 0.7071) calc(var(--bR) * 0.7071),
    calc(100% - var(--b1) + var(--bR) * 0.8315) calc(var(--bR) * 0.5556),
    calc(100% - var(--b1) + var(--bR) * 0.9239) calc(var(--bR) * 0.3827),
    calc(100% - var(--b1) + var(--bR) * 0.9808) calc(var(--bR) * 0.1951),
    calc(100% - var(--b1) + var(--bR)) 0px,
    100% 0,
    100% calc(50% - var(--dn-h) / 2 - var(--dn-r)),
    calc(100% - var(--dn-r) * 0.0761) calc(50% - var(--dn-h) / 2 - var(--dn-r) * 0.6173),
    calc(100% - var(--dn-r) * 0.2929) calc(50% - var(--dn-h) / 2 - var(--dn-r) * 0.2929),
    calc(100% - var(--dn-r) * 0.6173) calc(50% - var(--dn-h) / 2 - var(--dn-r) * 0.0761),
    calc(100% - var(--dn-r)) calc(50% - var(--dn-h) / 2),
    calc(100% - var(--dn-w) + var(--dn-r)) calc(50% - var(--dn-h) / 2),
    calc(100% - var(--dn-w) + var(--dn-r) * 0.6173) calc(50% - var(--dn-h) / 2 + var(--dn-r) * 0.0761),
    calc(100% - var(--dn-w) + var(--dn-r) * 0.2929) calc(50% - var(--dn-h) / 2 + var(--dn-r) * 0.2929),
    calc(100% - var(--dn-w) + var(--dn-r) * 0.0761) calc(50% - var(--dn-h) / 2 + var(--dn-r) * 0.6173),
    calc(100% - var(--dn-w)) calc(50% - var(--dn-h) / 2 + var(--dn-r)),
    calc(100% - var(--dn-w)) calc(50% + var(--dn-h) / 2 + var(--dn-r) * -1.0),
    calc(100% - var(--dn-w) + var(--dn-r) * 0.0761) calc(50% + var(--dn-h) / 2 + var(--dn-r) * -0.6173),
    calc(100% - var(--dn-w) + var(--dn-r) * 0.2929) calc(50% + var(--dn-h) / 2 + var(--dn-r) * -0.2929),
    calc(100% - var(--dn-w) + var(--dn-r) * 0.6173) calc(50% + var(--dn-h) / 2 + var(--dn-r) * -0.0761),
    calc(100% - var(--dn-w) + var(--dn-r)) calc(50% + var(--dn-h) / 2),
    calc(100% + var(--dn-r) * -1.0) calc(50% + var(--dn-h) / 2),
    calc(100% + var(--dn-r) * -0.6173) calc(50% + var(--dn-h) / 2 + var(--dn-r) * 0.0761),
    calc(100% + var(--dn-r) * -0.2929) calc(50% + var(--dn-h) / 2 + var(--dn-r) * 0.2929),
    calc(100% + var(--dn-r) * -0.0761) calc(50% + var(--dn-h) / 2 + var(--dn-r) * 0.6173),
    100% calc(50% + var(--dn-h) / 2 + var(--dn-r)),
    100% 100%,
    0 100%
  );
}

.stage[data-focus="accueil"] .tile[data-tile="hero"] {
  /* La tuile ne porte plus ni fond ni filet : ils passent sur ::before, qui
     seul est découpé. clip-path s'applique APRÈS filter et rogne donc aussi
     la sortie du drop-shadow — posée sur l'élément découpé, l'ombre était
     entièrement supprimée. Ici la tuile n'est pas découpée : son ombre
     épouse la silhouette de ::before et déborde librement.
     L'ombre projetée par le texte reste, elle, cachée derrière ::before,
     qui est opaque et couvre toute la tuile. */
  background: none;
  border-color: transparent;
  box-shadow: none;
  /* Les bulles chevauchent le bord haut : rien ne doit les rogner. Le seul
     contenu qui débordait, le filigrane, porte désormais son propre masque. */
  overflow: visible;
  /* Valeurs calibrées sur le box-shadow des autres tuiles, en comparant les
     profils de luminance sous le bord : drop-shadow n'ayant pas
     d'étalement, l'opacité compense le -18px de spread. Écart mesuré :
     0,16 niveau de gris. */
  filter:
    drop-shadow(0 2px 4px rgba(0, 0, 0, 0.32))
    drop-shadow(0 13px 26px rgba(0, 0, 0, 0.22));
}

.stage[data-focus="accueil"] .tile[data-tile="hero"]::before {
  content: "";
  position: absolute;
  /* -1px : l'élément est calé sur la boîte de padding, on recouvre la bordure */
  inset: -1px;
  z-index: -1;
  border-radius: inherit;
  background: var(--tile-2);
  border: 1px solid var(--tile-line);
  box-shadow: inset 0 1px 0 var(--tile-lip);
  /* Le décroché doit tomber exactement sur la gouttière. En hauteur comme en
     largeur, les valeurs sont dérivées de --dn-*, posées sur .stage. */
  clip-path: var(--decoupe-hero);
}

/* Accueil — la composition de référence */
.stage[data-focus="accueil"] [data-tile="hero"]     { --x: 0; --y: 0; --w: 7; --h: 5; }
.stage[data-focus="accueil"] [data-tile="projets"]  { --x: 7; --y: 0; --w: 5; --h: 5; }
.stage[data-focus="accueil"] [data-tile="contact"]  { --x: 0; --y: 5; --w: 3; --h: 3; }
.stage[data-focus="accueil"] [data-tile="services"] { --x: 3; --y: 5; --w: 4; --h: 3; }
.stage[data-focus="accueil"] [data-tile="studio"]   { --x: 7; --y: 5; --w: 5; --h: 3; }

/* Projets au premier plan */
/* Réalisations : la vitrine prend toute la largeur, les quatre autres
   tuiles s'alignent dessous en bandeau. */
.stage[data-focus="projets"] [data-tile="projets"]  { --x:  0; --y: 0; --w: 12; --h: 6; }
.stage[data-focus="projets"] [data-tile="hero"]     { --x:  0; --y: 6; --w:  3; --h: 2; }
.stage[data-focus="projets"] [data-tile="services"] { --x:  3; --y: 6; --w:  3; --h: 2; }
.stage[data-focus="projets"] [data-tile="studio"]   { --x:  6; --y: 6; --w:  3; --h: 2; }
.stage[data-focus="projets"] [data-tile="contact"]  { --x:  9; --y: 6; --w:  3; --h: 2; }

/* Savoir-faire au premier plan */
.stage[data-focus="services"] [data-tile="services"] { --x: 0; --y: 0; --w: 8; --h: 8; }
.stage[data-focus="services"] [data-tile="hero"]     { --x: 8; --y: 0; --w: 4; --h: 2; }
.stage[data-focus="services"] [data-tile="projets"]  { --x: 8; --y: 2; --w: 4; --h: 2; }
.stage[data-focus="services"] [data-tile="studio"]   { --x: 8; --y: 4; --w: 4; --h: 2; }
.stage[data-focus="services"] [data-tile="contact"]  { --x: 8; --y: 6; --w: 4; --h: 2; }

/* Studio au premier plan */
.stage[data-focus="studio"] [data-tile="studio"]   { --x: 0; --y: 0; --w: 8; --h: 8; }
.stage[data-focus="studio"] [data-tile="hero"]     { --x: 8; --y: 0; --w: 4; --h: 2; }
.stage[data-focus="studio"] [data-tile="projets"]  { --x: 8; --y: 2; --w: 4; --h: 2; }
.stage[data-focus="studio"] [data-tile="services"] { --x: 8; --y: 4; --w: 4; --h: 2; }
.stage[data-focus="studio"] [data-tile="contact"]  { --x: 8; --y: 6; --w: 4; --h: 2; }

/* Contact au premier plan */
.stage[data-focus="contact"] [data-tile="contact"]  { --x: 0; --y: 0; --w: 8; --h: 8; }
.stage[data-focus="contact"] [data-tile="hero"]     { --x: 8; --y: 0; --w: 4; --h: 2; }
.stage[data-focus="contact"] [data-tile="projets"]  { --x: 8; --y: 2; --w: 4; --h: 2; }
.stage[data-focus="contact"] [data-tile="services"] { --x: 8; --y: 4; --w: 4; --h: 2; }
.stage[data-focus="contact"] [data-tile="studio"]   { --x: 8; --y: 6; --w: 4; --h: 2; }

/* Amplitudes de flottement, en pixels de déplacement maximal */
[data-tile="hero"]     { --par: 7px; }
[data-tile="projets"]  { --par: 4px; }
[data-tile="services"] { --par: 2px; }
[data-tile="studio"]   { --par: 3px; }
[data-tile="contact"]  { --par: 6px; }

/* --------------------------------------------------- Intérieur de tuile */
.tile__head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 20px;
}

.tile__label {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--fg-3);
  min-width: 0;
}

/* Intitulé roulant : quatre faces sur un cylindre, un cran par survol de la
   tuile. Les faces sont posées à 0°, −90°, −180° et −270° puis translatées
   d'une demi-hauteur vers l'avant ; faire tourner la cage les amène tour à
   tour face au lecteur. */
.roll {
  --roll-h: 13px;
  display: inline-block;
  height: var(--roll-h);
  line-height: var(--roll-h);
  perspective: 340px;
  vertical-align: top;
}

.roll__cage {
  position: relative;
  display: block;
  height: 100%;
  transform-style: preserve-3d;
  transform: rotateX(calc(var(--n, 0) * 90deg));
  transition: transform 640ms cubic-bezier(0.62, 0.02, 0.14, 1);
}

.roll__face {
  position: absolute;
  left: 0;
  top: 0;
  white-space: nowrap;
  backface-visibility: hidden;
  transform-origin: 50% 50%;
}

.roll__face:nth-child(1) { transform: translateZ(calc(var(--roll-h) / 2)); }
.roll__face:nth-child(2) { transform: rotateX(-90deg)  translateZ(calc(var(--roll-h) / 2)); }
.roll__face:nth-child(3) { transform: rotateX(-180deg) translateZ(calc(var(--roll-h) / 2)); }
.roll__face:nth-child(4) { transform: rotateX(-270deg) translateZ(calc(var(--roll-h) / 2)); }

.tile__grow {
  width: 26px;
  height: 26px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 8px;
  border: 1px solid var(--tile-line);
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease),
              transform 0.4s var(--bento-ease);
}

.tile__grow svg { width: 12px; height: 12px; }
.tile:hover .tile__grow { color: var(--fg-2); border-color: var(--line-2); }
.tile[data-open="true"] .tile__grow { display: none; }

.tile__body {
  flex: 1;
  min-height: 0;
  /* `overflow-y: auto` force l'autre axe à devenir lui aussi un contexte de
     défilement : on ne peut pas y laisser `visible`. Le rognage tombe donc
     sur la boîte de padding — et un bouton posé à ras du bord se faisait
     trancher en grossissant au survol.
     Le padding étant *à l'intérieur* du rognage, on écarte la boîte de 10 px
     et on repousse le contenu d'autant : rien ne bouge à l'écran, mais la
     limite du rognage passe 10 px plus loin. Le padding des tuiles vaut au
     minimum 13 px, il absorbe le débord.
     10 et non 6 : à 6 px l'ombre du bouton survolé était encore rognée de
     1,6 niveau de gris, à 10 px l'écart tombe à 0,5 — mesuré contre un
     rognage assez large pour ne rien couper. */
  margin-inline: -10px;
  padding-inline: 10px;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--line-2) transparent;
}

.tile__body::-webkit-scrollbar { width: 6px; }
.tile__body::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 10px; }
.tile__body::-webkit-scrollbar-track { background: transparent; }

/* Mode aperçu : une tuile en retrait ne montre qu'un résumé. La condition
   porte sur data-open et non sur une largeur — une tuile au premier plan
   doit montrer son contenu entier, même dans une fenêtre étroite, et rien
   ne doit basculer pendant qu'on survole le menu.
   Bloc réservé au plan de travail : en pile, ces règles n'existent pas et
   il n'y a donc rien à annuler. */
@media (min-width: 1041px) and (min-height: 641px) {
  .tile[data-open="false"] .on-wide  { display: none !important; }
  .tile[data-open="true"]  .on-narrow { display: none !important; }

  /* Aperçu du hero : deux lignes de chapô plutôt qu'une phrase coupée */
  .tile[data-open="false"] .hero-t__lead {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    margin-top: 0.6rem;
  }

  /* Contact : bouton en aperçu, formulaire en grand */
  [data-tile="contact"][data-open="true"]  .contact-t__big  { display: none; }
  [data-tile="contact"][data-open="false"] .contact-t__form { display: none; }

  /* Savoir-faire : les familles descendent en pied de tuile */
  [data-tile="services"][data-open="true"] .tile__body { display: flex; flex-direction: column; }
  [data-tile="services"][data-open="true"] .svc { flex: 1; }
  [data-tile="services"][data-open="true"] .svc__families { margin-top: auto; padding-top: 0.6rem; }
  [data-tile="services"][data-open="true"] .svc__fam { padding: 1.1rem; }
}

/* ------------------------------------------------------------ Tuile hero */
.hero-t__title {
  font-size: clamp(1.3rem, 6.2cqw, 3.6rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  font-weight: 600;
  text-wrap: balance;
}

.hero-t__title em { font-style: normal; color: var(--fg-3); }

.hero-t__lead {
  color: var(--fg-2);
  font-size: clamp(0.8rem, 1.5cqw, 1.05rem);
  line-height: 1.55;
  max-width: 46ch;
  margin-top: 0.9rem;
}

.hero-t__cta { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.5rem; }

.hero-t__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.6rem;
  margin-top: 1.6rem;
  padding-top: 1rem;
  border-top: 1px solid var(--tile-line);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--fg-3);
}

[data-tile="hero"] .tile__body { display: flex; flex-direction: column; }
[data-tile="hero"] .hero-t__meta { margin-top: auto; }



/* Filigrane de la marque dans la tuile hero */
.hero-t__filigrane {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

/* Le ::before de la tuile est au z-index -1, le filigrane au-dessus : sans
   rognage propre il peignait dans l'encoche, là où la carte est justement
   creusée. Il reprend donc la silhouette exacte.
   inset: 0 le cale sur la boîte de bordure moins 1 px, alors que la découpe
   est en pourcentages de la boîte de bordure : l'écart d'un pixel sur un
   congé de 14 est invisible, et le filigrane s'arrête de toute façon bien
   avant les entailles du haut. */
.stage[data-focus="accueil"] .tile[data-tile="hero"] .hero-t__filigrane {
  clip-path: var(--decoupe-hero);
}

.hero-t__mark {
  position: absolute;
  right: -4%;
  bottom: -10%;
  width: 42cqw;
  max-width: 300px;
  color: var(--fg);
  opacity: 0.04;
  pointer-events: none;
}

/* ------------------------------------------------ Bulles de profil ------
   Trois pastilles rondes calées sur le bord haut de la tuile hero. Le
   clip-path de ::before y creuse autant d'entailles de rayon --bR, si bien
   que la matière de la carte s'écarte autour de chaque bulle : l'anneau
   de fond visible autour de chacune fait --ba.
   Les positions horizontales (--b1..--b3) et le rayon sont partagés avec
   le polygone : une seule source pour la découpe et pour la pastille. */
.bulles {
  position: absolute;
  /* -1px : la tuile positionne ses enfants depuis la boîte de padding, on
     recale sur la boîte de bordure pour coïncider avec le polygone. */
  top: -1px;
  right: calc(var(--b1) - var(--bd) / 2 - 1px);
  transform: translateY(-50%);
  display: flex;
  flex-direction: row-reverse;   /* la première ancre se pose à droite */
  gap: calc(var(--bpas) - var(--bd));
  z-index: 6;
}

.bulle {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--bd);
  height: var(--bd);
  border-radius: 50%;
  color: var(--fg-2);
  text-decoration: none;
  transition: transform 0.42s var(--bento-ease), color 0.3s var(--ease);
}

.bulle__pastille {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--tile-2);
  border: 1px solid var(--tile-line);
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    0 2px 4px -3px rgba(0, 0, 0, 0.5),
    0 8px 16px -12px rgba(0, 0, 0, 0.7);
  transition: background-color 0.3s var(--ease), border-color 0.3s var(--ease);
}

.bulle svg { width: 19px; height: 19px; display: block; }

/* Malt n'a pas de glyphe : sa signature est son mot-marque en bas de casse. */
.bulle__sigle {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

/* Intitulé au survol, au-dessus de la bulle : la carte reste dégagée. */
.bulle__nom {
  position: absolute;
  bottom: calc(100% + 5px);
  left: 50%;
  transform: translate(-50%, 3px);
  font-family: var(--font-mono);
  font-size: 0.54rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  /* --fg-3 était calibré sur le fond des tuiles ; ici l'intitulé se pose sur
     le plan, plus sombre en clair, où il ne passait AA que de 0,02. */
  color: var(--fg-2);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s var(--ease), transform 0.28s var(--bento-ease);
}

.bulle:hover,
.bulle:focus-visible { color: var(--fg); transform: translateY(-3px); }

.bulle:hover .bulle__pastille,
.bulle:focus-visible .bulle__pastille {
  /* La pastille doit rester opaque : elle chevauche la gouttière. On la
     décale donc vers --fg plutôt que d'y poser un voile translucide. */
  background: color-mix(in srgb, var(--tile-2) 92%, var(--fg));
  border-color: var(--line-2);
}

.bulle:hover .bulle__nom,
.bulle:focus-visible .bulle__nom { opacity: 1; transform: translate(-50%, 0); }

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

/* Les entailles n'existent que dans la composition d'accueil : ailleurs la
   tuile hero est rectangulaire et les bulles n'auraient pas de logement. */
.stage:not([data-focus="accueil"]) .bulles { display: none; }


/* -------------------------------------------------------- Zone projets --
   Ce n'est pas une tuile : un projet = une carte. Le conteneur disparaît
   complètement (ni fond, ni filet, ni ombre, ni intitulé) et ce sont les
   cartes elles-mêmes qui deviennent les objets du bento. Le sélecteur est
   plus spécifique que .tile[data-open="true"], qui rétablirait un fond. */
.stage .tile[data-tile="projets"] {
  background: none;
  border: 0;
  box-shadow: none;
  padding: 0;
  overflow: visible;
}

.stage .tile[data-tile="projets"] .tile__head { display: none; }
.stage .tile[data-tile="projets"] .tile__body { overflow: visible; gap: 0; }


/* Un jeu de cartes empilées. Chaque carte est décalée de --step vers le bas
   et passe DEVANT la précédente : seule sa bandeau-titre dépasse au repos,
   trois cartes se lisent d'un coup. Au survol, la carte s'élargit et passe
   au premier plan — inutile d'écarter les autres, elle passe devant. */
.deck {
  --visibles: 3;      /* cartes montrées d'un coup                       */
  --recouvre: 0.45;   /* part d'une carte que la suivante recouvre       */
  --page: 0;

  /* Géométrie en pourcentages de l'emplacement, jamais en pixels fixes :
     la hauteur du bloc dépend de la fenêtre, et des tailles figées
     débordaient sur les écrans moins hauts.
     h + (n-1)·pas = 100 %, avec pas = (1 − recouvrement)·h              */
  --carte-h: calc(100% / (1 + (var(--visibles) - 1) * (1 - var(--recouvre))));
  --step: calc(var(--carte-h) * (1 - var(--recouvre)));

  position: relative;
  height: 100%;
  /* Les cartes des autres pages doivent être masquées, mais un rognage trop
     serré tranche les ombres portées : le bord devient droit et les angles
     perdent leur arrondi.
     overflow: hidden ne sait pas rogner un seul axe — dès qu'un axe est
     masqué, l'autre l'est aussi. clip-path accepte des valeurs négatives et,
     surtout, des marges différentes par côté :
       en haut  : 50 px — portée mesurée de l'ombre d'une carte survolée,
                  qui atteint le niveau du plan à 46 px ;
       en bas   : juste sous la gouttière, pour cacher la page suivante sans
                  déborder sur la tuile d'en dessous ;
       de côté  : large, rien n'a besoin d'y être masqué. */
  clip-path: inset(-50px -40px -14px -40px);
}

.deck__card {
  position: absolute;
  left: var(--inset);
  right: var(--inset);
  /* Le décalage de page glisse toute la pile d'un cran de --visibles */
  top: calc((var(--i) - var(--page) * var(--visibles)) * var(--step));
  height: var(--carte-h);
  /* --rang : la place dans la pile, 0 au premier plan. Il commande à lui
     seul l'ordre, le retrait latéral et la clarté — les trois signaux de
     profondeur restent ainsi solidaires.
     La valeur ci-dessous est celle du repos : la dernière carte de la page
     mène. bento.js la remplace au survol pour que la carte survolée passe
     devant, et l'y laisse ensuite. */
  --rang: calc(var(--visibles) - 1 - var(--i) + var(--page) * var(--visibles));
  z-index: calc(10 - var(--rang));
  /* Largeur dégressive : chaque carte du fond est un peu plus étroite que
     celle de devant. C'est ce liseré décalé de part et d'autre qui fait
     lire une pile plutôt qu'une liste. */
  --inset: calc(var(--rang) * 5px);
  padding: clamp(13px, 1.25vw, 22px);
  border-radius: clamp(14px, 1.3vw, 20px);
  border: 1px solid var(--tile-line);
  background-color: var(--tile);
  /* Les cartes s'éclaircissent en venant vers l'avant. Du rang et non de
     l'indice : sinon les cartes de la page 2 seraient plus claires que
     celles de la page 1, l'indice ne cessant de croître. */
  background-image: linear-gradient(
    rgba(255, 255, 255, calc((var(--visibles) - 1 - var(--rang)) * 0.022)),
    rgba(255, 255, 255, calc((var(--visibles) - 1 - var(--rang)) * 0.022)));
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    0 -6px 20px -6px rgba(0, 0, 0, 0.65),
    0 2px 4px -2px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  overflow: hidden;
  transition:
    left 0.55s var(--bento-ease),
    right 0.55s var(--bento-ease),
    top 0.55s var(--bento-ease),
    transform 0.45s var(--bento-ease),
    box-shadow 0.35s var(--ease),
    background-color 0.35s var(--ease),
    opacity 0.3s var(--ease);
}

/* Élargissement plutôt que scale : le corps de tuile masque son
   débordement horizontal, un agrandissement serait rogné. */
.deck__card:hover,
.deck__card:focus-visible {
  --inset: 0px;
  z-index: 40;
  transform: translateY(-4px);
  background-color: var(--tile-2);
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    0 -10px 30px -6px rgba(0, 0, 0, 0.8),
    0 12px 26px -14px rgba(0, 0, 0, 0.6);
}

/* Cartes des autres pages : elles restent dans le flux (c'est ce qui rend
   le glissement visible) mais ne s'affichent pas. La disparition est rapide :
   avec 50 px de marge de rognage en haut, un effacement lent les laisserait
   voir au-dessus du bloc pendant le glissement. */
.deck__card.is-hors-page {
  opacity: 0;
  pointer-events: none;
  transition:
    opacity 0.12s var(--ease),
    top 0.55s var(--bento-ease),
    left 0.55s var(--bento-ease),
    right 0.55s var(--bento-ease);
}

/* ---------------------------------------------------- Pastilles de page */
.deck__dots {
  position: absolute;
  top: 50%;
  /* Centrées dans la découpe pratiquée au bord droit de la tuile hero */
  left: calc(-1 * (var(--gap) + var(--dn-w, 34px) / 2));
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  gap: 9px;
  z-index: 20;
}

.deck__dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--line-2);
  cursor: pointer;
  transition: background-color 0.35s var(--ease), height 0.4s var(--bento-ease);
}

.deck__dot:hover { background: var(--fg-2); }

.deck__dot[aria-current="true"] {
  height: 22px;
  border-radius: 100px;
  background: var(--fg);
}

/* Le bandeau d'identité suit la bande visible. Au repos chaque carte est
   recouverte par le bas, où il n'y a rien : le bandeau reste en haut. Mais
   dès qu'une carte du dessus passe devant, elle mange précisément ce haut —
   le bandeau descend alors de --recouvre, soit exactement la hauteur avalée.
   `top` en pourcentage sur un enfant positionné se rapporte à la hauteur de
   la carte, qui est définie : c'est ce qui permet de l'exprimer sans JS.
   La description, elle, ne bouge pas — elle ne s'affiche qu'au survol, et
   une carte survolée est la meneuse, donc jamais recouverte. */
.deck__strip {
  position: relative;
  top: calc(var(--couvert, 0) * var(--recouvre) * 100%);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  transition: top 0.5s var(--bento-ease);
}

.deck__thumb {
  width: 96px;
  flex: none;
  aspect-ratio: 4 / 3;
  border-radius: 9px;
  overflow: hidden;
  background: var(--desk-2);
  border: 1px solid var(--tile-line);
}

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

.deck__id { flex: 1; min-width: 0; display: grid; gap: 0.15rem; }

.deck__name {
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: -0.012em;
  line-height: 1.25;
}

.deck__year {
  font-family: var(--font-mono);
  font-size: 0.56rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.deck__tag {
  flex: none;
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: 0.53rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-3);
  border: 1px solid var(--tile-line);
  border-radius: 100px;
  padding: 0.13rem 0.45rem;
}

.deck__desc {
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--fg-2);
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--tile-line);
  /* Masquée au repos : la carte suivante ne doit laisser voir que de la
     surface nue, jamais une ligne de texte coupée en deux. */
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}

.deck__card:hover .deck__desc,
.deck__card:focus-visible .deck__desc { opacity: 1; }

/* ---------------------------------------------- Réalisations en vitrine --
   Au premier plan, la pile verticale devient un carrousel : une carte au
   centre, les autres de part et d'autre, en retrait et estompées. Il tourne
   seul, de droite à gauche, et boucle sans fin — c'est une vitrine, on n'y
   clique pas et il n'y a pas de détail à ouvrir.
   Tout se déduit de deux nombres posés par bento.js : --d, la distance
   signée à la carte active, et --dabs, sa valeur absolue. Position, échelle,
   opacité et ordre en découlent, ce qui rend la boucle gratuite : une carte
   qui saute de +3 à -3 traverse le centre à opacité nulle des deux côtés,
   donc invisible. */
/* La largeur de la carte est déclarée sur la tuile, pas sur le carrousel :
   les flèches, qui sont des sœurs du corps, s'en servent pour se caler juste
   à côté d'elle. Tuile et carrousel ont la même largeur, le pourcentage vaut
   donc la même chose des deux côtés. */
[data-tile="projets"][data-open="true"] {
  --carte-l: 57%;
  --carte-r: clamp(52px, 5.8vw, 92px);   /* rayon de la carte  */
  --carte-p: 6px;                        /* marge de la carte  */
}

[data-tile="projets"][data-open="true"] .deck {
  /* Écart entre deux cartes, en part de leur largeur. Contre-intuitif : c'est
     un écart *faible* qui fait moins dépasser les voisines. Grand, elles vont
     couvrir toute la bande laissée libre par la carte du centre jusqu'au bord
     de la tuile ; petit, elles restent tapies derrière elle et ne laissent
     voir qu'un liseré, le bord de la tuile redevenant vide. */
  --pas: 26%;
  /* Les pastilles occupent le bas : le carrousel se centre au-dessus. */
  height: calc(100% - 34px);
  /* Le fondu des bords éloigne les voisines mieux qu'un simple retrait :
     elles se dissolvent au lieu d'être tranchées.
     Il ne sert plus qu'à adoucir : les voisines s'arrêtent maintenant bien
     avant le bord, c'est --pas qui règle ce qui dépasse. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  /* La tuile est en overflow: visible — posé pour que les ombres de la pile
     débordent — donc rien ne retient les voisines, qui allaient peindre en
     translucide par-dessus le rail et les tuiles du bandeau. Le carrousel
     porte donc son propre rognage : net sur les côtés, c'est lui qui donne
     la fuite ; large en haut et en bas, pour les ombres. */
  clip-path: inset(-40px 0);
}

[data-tile="projets"][data-open="true"] .deck__dots { display: none; }

[data-tile="projets"][data-open="true"] .deck__card {
  --d: 0;
  --dabs: 0;
  left: 50%;
  right: auto;
  top: 50%;
  width: var(--carte-l);
  height: 88%;
  z-index: calc(10 - var(--dabs));
  opacity: calc(1 - var(--dabs) * 0.6);
  /* Seul objet de la tuile, la carte prend le fond clair des tuiles de
     premier plan plutôt que celui de la pile. */
  background-color: var(--tile-2);
  background-image: none;
  cursor: default;
  /* Un voile de flou sur les voisines : elles reculent, et leur texte cesse
     de concurrencer celui du centre. La carte active a --dabs à 0, donc
     blur(0) — sans effet. */
  filter: blur(calc(var(--dabs) * 1.4px));
  display: grid;
  /* Deux rangées souples encadrent le texte : elles le centrent en hauteur
     face à la vignette, qui traverse elle toute la carte. */
  grid-template-columns: 46% 1fr;
  grid-template-rows: 1fr auto auto auto 1fr;
  column-gap: clamp(18px, 2vw, 34px);
  /* La vignette touche presque le bord : c'est la carte qui se réduit à
     6 px de marge, le texte reprenant la sienne plus loin. */
  padding: var(--carte-p);
  border-radius: var(--carte-r);
  transform:
    translate(-50%, -50%)
    translateX(calc(var(--d) * var(--pas)))
    scale(calc(1 - var(--dabs) * 0.24));
  transition:
    transform 0.62s var(--bento-ease),
    opacity 0.62s var(--bento-ease),
    filter 0.62s var(--bento-ease);
}

/* Rien ne réagit au pointeur : la vitrine n'est pas une liste de liens.
   Ces règles l'emportent en spécificité sur .deck__card:hover. */
[data-tile="projets"][data-open="true"] .deck__card:hover,
[data-tile="projets"][data-open="true"] .deck__card:focus-visible {
  background-color: var(--tile-2);
  transform:
    translate(-50%, -50%)
    translateX(calc(var(--d) * var(--pas)))
    scale(calc(1 - var(--dabs) * 0.24));
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    0 -6px 20px -6px rgba(0, 0, 0, 0.65),
    0 2px 4px -2px rgba(0, 0, 0, 0.45);
}

/* Le bandeau s'efface comme boîte pour que ses enfants prennent leur place
   dans la grille de la carte : la vignette à gauche sur toute la hauteur,
   le texte à droite. */
[data-tile="projets"][data-open="true"] .deck__strip { display: contents; }

[data-tile="projets"][data-open="true"] .deck__thumb {
  grid-area: 1 / 1 / -1 / 2;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  /* Concentrique à la carte : deux arrondis restent parallèles quand le
     rayon intérieur vaut l'extérieur moins l'écart. Un rayon plus serré
     ouvre l'angle, et la vignette cesse de suivre le bord. */
  border-radius: calc(var(--carte-r) - var(--carte-p));
}

[data-tile="projets"][data-open="true"] .deck__tag {
  grid-area: 2 / 2;
  justify-self: start;
  margin-bottom: 0.85rem;
}

/* La carte n'a plus que 6 px de marge, pour la vignette : le texte reprend
   la sienne de son côté. */
[data-tile="projets"][data-open="true"] .deck__tag,
[data-tile="projets"][data-open="true"] .deck__id,
[data-tile="projets"][data-open="true"] .deck__desc {
  padding-right: clamp(16px, 1.8vw, 30px);
}

[data-tile="projets"][data-open="true"] .deck__id { grid-area: 3 / 2; }

[data-tile="projets"][data-open="true"] .deck__desc {
  grid-area: 4 / 2;
  align-self: start;
  opacity: 1;
  font-size: 0.85rem;
  /* La colonne de texte est large ; sans bride la description tient sur une
     ligne et le bloc paraît vide. Le filet de séparation n'a plus lieu
     d'être, la description n'est plus une révélation au survol. */
  max-width: 34ch;
  margin-top: 0.9rem;
  padding-top: 0;
  border-top: 0;
}

[data-tile="projets"][data-open="true"] .deck__name { font-size: clamp(1.1rem, 1.5vw, 1.5rem); }

/* Commandes de la vitrine. Elles n'existent qu'au premier plan : ailleurs la
   tuile est un aperçu, il n'y a rien à parcourir. */
.vitrine-fleche,
.vitrine-points { display: none; }

[data-tile="projets"][data-open="true"] .vitrine-fleche {
  position: absolute;
  top: calc(50% - 17px);   /* 34 px de pastilles en bas : on remonte d'autant */
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--tile-line);
  background: var(--tile-2);
  color: var(--fg-2);
  cursor: pointer;
  z-index: 20;
  box-shadow:
    inset 0 1px 0 var(--tile-lip),
    0 2px 4px -3px rgba(0, 0, 0, 0.5),
    0 8px 16px -12px rgba(0, 0, 0, 0.7);
  transition: transform 0.4s var(--bento-ease), color 0.3s var(--ease),
              border-color 0.3s var(--ease);
}

/* Calées contre la carte du centre : son bord est à (100 % − --carte-l) / 2,
   la flèche fait 40 px et on laisse 12 px de jeu. */
[data-tile="projets"][data-open="true"] .vitrine-fleche--avant {
  left: calc((100% - var(--carte-l)) / 2 - 52px);
}

[data-tile="projets"][data-open="true"] .vitrine-fleche--apres {
  right: calc((100% - var(--carte-l)) / 2 - 52px);
}

.vitrine-fleche svg { width: 16px; height: 16px; }

.vitrine-fleche:hover,
.vitrine-fleche:focus-visible {
  color: var(--fg);
  border-color: var(--line-2);
  transform: scale(1.06);
}

.vitrine-fleche:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }

[data-tile="projets"][data-open="true"] .vitrine-points {
  position: absolute;
  bottom: 4px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 7px;
  z-index: 20;
}

.vitrine-point {
  width: 7px;
  height: 7px;
  padding: 0;
  border: 0;
  border-radius: 100px;
  background: var(--line-2);
  cursor: pointer;
  transition: width 0.4s var(--bento-ease), background-color 0.35s var(--ease);
}

.vitrine-point:hover { background: var(--fg-2); }

.vitrine-point[aria-current="true"] { width: 20px; background: var(--fg); }

.vitrine-point:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }

/* ------------------------------------------------------- Tuile services */
.svc { display: grid; gap: 1.1rem; }

.svc__group { display: grid; gap: 0.5rem; }

.svc__title {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.svc__chips { display: flex; flex-wrap: wrap; gap: 0.34rem; }

.chip {
  font-size: 0.72rem;
  letter-spacing: -0.005em;
  color: var(--fg-2);
  background: var(--surface);
  border: 1px solid var(--tile-line);
  border-radius: 8px;
  padding: 0.28rem 0.6rem;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease),
              background-color 0.3s var(--ease);
}

.chip:hover { color: var(--fg); border-color: var(--line-2); background: var(--surface-2); }

.svc__families { display: grid; gap: 0.6rem; margin-top: 0.4rem; }

@container tile (min-width: 620px) {
  .svc__families { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.svc__fam {
  padding: 0.85rem;
  border-radius: 12px;
  border: 1px solid var(--tile-line);
  background: var(--surface);
  display: grid;
  gap: 0.3rem;
  align-content: start;
}

.svc__fam h4 { font-size: 0.85rem; letter-spacing: -0.015em; }
.svc__fam p { font-size: 0.76rem; color: var(--fg-2); line-height: 1.5; }



/* --------------------------------------------------------- Tuile studio */
.studio__text { color: var(--fg-2); font-size: 0.82rem; line-height: 1.6; max-width: 58ch; }
.studio__text strong { color: var(--fg); font-weight: 500; }
.studio__text p + p { margin-top: 0.7rem; }

.studio__facts {
  display: grid;
  gap: 0;
  margin: 1.1rem 0 0;
  border-top: 1px solid var(--tile-line);
}

.studio__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  padding-block: 0.55rem;
  border-bottom: 1px solid var(--tile-line);
}

.studio__row dt {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg-3);
  flex: none;
}

.studio__row dd { margin: 0; font-size: 0.8rem; text-align: right; color: var(--fg); }

/* -------------------------------------------------------- Tuile contact */
.contact-t { display: flex; flex-direction: column; height: 100%; }

.contact-t__pitch { color: var(--fg-2); font-size: 0.82rem; line-height: 1.55; }

.contact-t__big {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  margin-top: auto;
  padding: 0.7rem 0.75rem 0.7rem 0.7rem;
  border-radius: 100px;
  border: 1px solid var(--tile-line);
  background: var(--surface);
  color: var(--fg);
  cursor: pointer;
  font-size: 0.82rem;
  text-align: left;
  transition: border-color 0.35s var(--ease), background-color 0.35s var(--ease);
}

.contact-t__big:hover { border-color: var(--line-2); background: var(--surface-2); }

.contact-t__plus {
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--fg);
  color: var(--bg);
  transition: transform 0.45s var(--bento-ease);
}

.contact-t__big:hover .contact-t__plus { transform: rotate(90deg); }
.contact-t__plus svg { width: 13px; height: 13px; }

.contact-t__form { display: grid; gap: 0.8rem; margin-top: 1rem; }

.contact-t__grid { display: grid; gap: 0.8rem; }
@container tile (min-width: 620px) {
  .contact-t__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.contact-t__coord {
  display: grid;
  gap: 0.75rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--tile-line);
}

.contact-t__coord div { display: grid; gap: 0.15rem; }

.contact-t__k {
  font-family: var(--font-mono);
  font-size: 0.56rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.contact-t__v { font-size: 0.82rem; }

/* ------------------------------------------------------------ Pied fin */
.tile__foot {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.2rem;
  align-items: center;
  justify-content: space-between;
  padding-top: 0.7rem;
  border-top: 1px solid var(--tile-line);
  font-family: var(--font-mono);
  font-size: 0.55rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.tile__foot a { transition: color 0.3s var(--ease); }
.tile__foot a:hover { color: var(--fg); }

/* ======================================================================
   Repli : sous 1040 px de large ou 640 px de haut, la mise en scène n'a
   plus de sens. Les tuiles s'empilent et la page redevient défilable.
   ====================================================================== */
@media (max-width: 1040px), (max-height: 640px) {
  html.app, body.app { height: auto; overflow: visible; }

  .app-frame {
    position: static;
    inset: auto;
    --rail-w: 52px;
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    grid-template-areas:
      "logo  top"
      "rail  rail"
      "stage stage";
    min-height: 100svh;
    border-radius: clamp(16px, 3vw, 24px);
    margin: clamp(8px, 2vw, 16px);
    perspective: none;
  }

  .rail__logo .k-mark { width: 22px; }

  /* Le rail devient une barre de pastilles horizontale */
  .rail { z-index: 40; }

  .rail__panel {
    position: static;
    width: auto;
    flex-direction: row;
    align-items: center;
    border-radius: 100px;
    padding: 6px;
  }

  .rail:hover .rail__panel,
  .rail:has(:focus-visible) .rail__panel,
  .rail.is-open .rail__panel { width: auto; border-radius: 100px; }

  .rail__nav {
    flex-direction: row;
    margin: 0;
    gap: 3px;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 90%, transparent);
    mask-image: linear-gradient(90deg, #000 90%, transparent);
  }

  .rail__nav::-webkit-scrollbar { display: none; }

  .navi { width: auto; height: 38px; padding: 0 0.9rem; border-radius: 100px; }
  .navi__label { opacity: 1; transform: none; font-size: 0.8rem; }
  .navi__ico { display: none; }

  .rail__foot { display: none; }
  .topbar__where { display: none; }

  /* La scène redevient un empilement classique */
  .stage {
    display: flex;
    flex-direction: column;
    gap: var(--frame-gap);
    transform: none !important;
  }

  .tile {
    position: static;
    width: auto;
    height: auto;
    min-height: 200px;
    transform: none;
    order: var(--order, 0);
    /* Les requêtes de conteneur sont neutralisées : en pile, chaque tuile
       occupe toute la largeur et doit montrer son contenu complet, quelle
       que soit sa largeur en pixels. */
    container-type: normal;
    transition: background-color 0.45s var(--ease), border-color 0.45s var(--ease),
                box-shadow 0.45s var(--ease);
  }

  .tile__body { overflow: visible; }

  /* Pas de découpe en pile. Le sélecteur doit égaler celui de l'état
     Accueil : une media query n'ajoute aucune spécificité. */
  .stage[data-focus="accueil"] .tile[data-tile="hero"]::before { display: none; }

  /* Même spécificité que la règle d'accueil : une media query n'en ajoute
     aucune. En pile la tuile n'a plus la forme du polygone. */
  .stage[data-focus="accueil"] .tile[data-tile="hero"] .hero-t__filigrane {
    clip-path: none;
  }

  .stage[data-focus="accueil"] .tile[data-tile="hero"],
  .stage .tile[data-tile="hero"] {
    background: var(--tile);
    border-color: var(--tile-line);
    clip-path: none;
    filter: none;
    box-shadow:
      inset 0 1px 0 var(--tile-lip),
      0 2px 4px -2px rgba(0, 0, 0, 0.5),
      0 14px 30px -18px rgba(0, 0, 0, 0.75);
  }


  /* En pile, chaque tuile montre tout : le bloc « aperçu » ci-dessus est
     borné au plan de travail, il n'y a rien à annuler ici.
     Le jeu de cartes redevient une liste : superposer n'a pas de sens
     quand la page défile. */
  .contact-t__big { display: none; }

  /* En pile : plus de superposition, plus de pagination. Les hauteurs en
     pourcentage n'ont plus de référence, on repasse en hauteur naturelle. */
  .deck {
    height: auto;
    min-height: 0;
    overflow: visible;
    display: grid;
    gap: 0.6rem;
  }

  /* En pile, les cartes ne se recouvrent plus : le bandeau reste en place.
     Sa hauteur de référence disparaîtrait de toute façon avec height:auto. */
  .deck__strip { position: static; top: auto; }

  .deck__card {
    position: static;
    height: auto;
    --inset: 0px;
    transform: none !important;
    opacity: 1 !important;
    padding-bottom: clamp(13px, 1.25vw, 22px);
    box-shadow: inset 0 1px 0 var(--tile-lip),
                0 2px 4px -2px rgba(0, 0, 0, 0.5);
  }

  .deck__card.is-hors-page { opacity: 1 !important; pointer-events: auto; }

  /* En pile, pas de vitrine : les cartes reprennent le fil du document.
     Même spécificité que les règles du carrousel, une media query n'en
     ajoutant aucune. */
  [data-tile="projets"][data-open="true"] .deck__card {
    display: block;
    width: auto;
    height: auto;
    opacity: 1;
    transform: none;
    cursor: pointer;
  }

  [data-tile="projets"][data-open="true"] .deck__strip {
    display: flex;
    align-items: center;
    gap: 0.75rem;
  }


  [data-tile="projets"][data-open="true"] .deck__desc { max-width: none; }

  [data-tile="projets"][data-open="true"] .vitrine-fleche,
  [data-tile="projets"][data-open="true"] .vitrine-points { display: none; }
  .deck__dots { display: none; }

  /* Sans découpe, les bulles ne peuvent plus s'encastrer : elles redeviennent
     une simple rangée en pied de la tuile hero. */
  .bulles {
    position: static;
    transform: none;
    flex-direction: row;
    gap: 0.6rem;
    margin-top: 1.4rem;
  }

  .bulle__nom { position: static; transform: none; opacity: 1; }
  .bulle { width: auto; height: auto; gap: 0.5rem; grid-auto-flow: column; }
  .bulle__pastille { width: 38px; height: 38px; }

  /* Sans survol, la description ne se révélerait jamais */
  .deck__desc { opacity: 1; }

  /* La section choisie remonte en tête de pile */
  .stage[data-focus="projets"]  [data-tile="projets"]  { --order: -1; }
  .stage[data-focus="services"] [data-tile="services"] { --order: -1; }
  .stage[data-focus="studio"]   [data-tile="studio"]   { --order: -1; }
  .stage[data-focus="contact"]  [data-tile="contact"]  { --order: -1; }

  .tile[data-open="false"] { cursor: default; }
  .tile__grow { display: none; }
  .hero-t__mark { display: none; }
}

/* Très petits écrans : la vignette mange trop de largeur */
@media (max-width: 420px) {
  .deck__thumb { display: none; }
}

/* ------------------------------------------------------ Mouvement réduit */
@media (prefers-reduced-motion: reduce) {
  .tile { transform: none !important; }
  .roll__cage { transition: none; }
  .stage.is-morphing .tile__body { opacity: 1; }
}
