* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* ─── UI overlay : 4 coins (identité, rôle, statut, contact) + dégradé d'arrière-plan ─── */
.ui-overlay {
  position: fixed;
  inset: 0;
  pointer-events: none;
  /* z-index supérieur au canvas onde (9999) et au layer des contours (10000)
     → les textes des coins passent au-dessus de l'onde au clic. */
  z-index: 10001;
  font-family: 'Suisse Int\'l', 'Suisse Intl', 'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 13px;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.85);
}

/* Assombrissement global des projets au survol d'un texte UI. */
.tile-darken-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  pointer-events: none;
  z-index: 50; /* au-dessus des tiles (z 1), en-dessous du canvas onde + UI overlay */
  opacity: 0;
  transition: opacity 0.45s ease;
}

.tile-darken-overlay.is-active {
  opacity: 1;
}


/* Léger dégradé sombre en haut pour appuyer la lisibilité des textes des corners. */
.ui-overlay::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 140px;
  pointer-events: none;
  z-index: -1;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.2) 60%, transparent 100%);
}

/* Dégradé "qui vient de l'extérieur" — 4 ellipses radiales depuis chaque coin
   qui s'estompent vers le centre, esprit visuel proche de l'onde au clic. */
.ui-grad-bg {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 70% 55% at 0% 0%,   rgba(255, 255, 255, 0.07), transparent 60%),
    radial-gradient(ellipse 70% 55% at 100% 0%, rgba(255, 255, 255, 0.07), transparent 60%),
    radial-gradient(ellipse 70% 55% at 0% 100%, rgba(255, 255, 255, 0.07), transparent 60%),
    radial-gradient(ellipse 70% 55% at 100% 100%, rgba(255, 255, 255, 0.07), transparent 60%);
  mix-blend-mode: screen;
}

.ui-corner {
  position: absolute;
  /* Padding horizontal = 48px (= GAP entre tuiles dans app.js) → textes alignés sur la grille */
  padding: 28px 48px;
  pointer-events: auto;
  line-height: 1;
}

.ui-corner--tl { top: 0; left: 0; }
.ui-corner--tr { top: 0; right: 0; }

.ui-corner a {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

.ui-corner a:hover {
  color: #fff;
}

/* "Lead Designer UI" et le nom du projet (dans "pour <Nom>") en rouge. */
.ui-corner__role,
.ui-corner__suffix-name {
  color: #ff3030;
}


html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #000;
  font-family: -apple-system, BlinkMacSystemFont, sans-serif;
  cursor: none;
}

#cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(180, 180, 180, 0.55);
  pointer-events: none;
  z-index: 1000;
  /* Transform set en JS à chaque mousemove (perf : GPU compositing, pas de layout). */
  transition:
    width 0.4s cubic-bezier(0.4, 0, 0.4, 1),
    height 0.4s cubic-bezier(0.4, 0, 0.4, 1),
    background 0.4s ease;
  will-change: transform, width, height, background;
}

#cursor.locked {
  width: 8px;
  height: 8px;
  background: #fff;
}

.viewport {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: #000;
}

.scroller {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  /* Pas de will-change : .scroller n'est jamais transformé (le transform est sur chaque tile). */
}

.tile {
  position: absolute;
  will-change: transform;
  user-select: none;
  border-radius: var(--tile-radius-outer, 32px);
  z-index: 1;
  transition: --glow-alpha 0.6s ease-out, opacity 0.6s ease;
}

/* Apparition au load : chaque tuile remonte de 60px en fadant, staggered via --enter-delay.
   `translate` (Transform Level 2) se compose avec le `transform: translate3d` du défilement. */
@keyframes tile-appear {
  from { opacity: 0; translate: 0 60px; }
  to   { opacity: 1; translate: 0 0; }
}

.tile {
  opacity: 0;
  animation: tile-appear 0.9s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: var(--enter-delay, 0s);
}

/* Glow breathing au survol — coexiste avec tile-appear via animation multi-name. */
.tile:hover {
  animation:
    tile-appear 0.9s cubic-bezier(0.22, 1, 0.36, 1) forwards,
    glow-breathe 2.6s ease-in-out infinite;
  animation-delay: var(--enter-delay, 0s), 0s;
}

/* @property nécessaire pour animer/transitioner une custom property numérique */
@property --glow-alpha {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* Respiration au hover : intensité oscille entre 50% et 100% */
@keyframes glow-breathe {
  0%, 100% { --glow-alpha: 0.5; }
  50%      { --glow-alpha: 1; }
}

.tile-frame {
  position: relative;
  width: 100%;
  height: 100%;
  background: #000;
  padding: 12px;
  border-radius: var(--tile-radius-outer, 32px);
  /* Glow multi-couleurs : 3 ombres décalées avec 3 teintes extraites de la maquette
     (haut/milieu/bas). Le contour 1 px est désormais un pseudo-élément ::before
     avec un radial-gradient qui suit le curseur global. */
  --glow-spread: -120px;
  --glow-y: 120px;
  box-shadow:
    -60px var(--glow-y) 90px var(--glow-spread) color-mix(in srgb,
      var(--tile-glow-1, transparent) calc(var(--glow-alpha) * 100%), transparent),
       0px var(--glow-y) 100px var(--glow-spread) color-mix(in srgb,
      var(--tile-glow-2, transparent) calc(var(--glow-alpha) * 100%), transparent),
     60px var(--glow-y) 90px var(--glow-spread) color-mix(in srgb,
      var(--tile-glow-3, transparent) calc(var(--glow-alpha) * 100%), transparent);
  transition: transform 1.2s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Contour 1 px : radial-gradient (FFF 80% → FFF 20%) centré sur la position du curseur,
   projetée en coords locales (% de la tile) par le rAF — tous les contours réagissent
   en même temps au mouvement de la souris globale. */
.tile-frame::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: radial-gradient(
    circle at var(--cursor-x, 50%) var(--cursor-y, 50%),
    rgba(255, 255, 255, 0.8) 0%,
    rgba(255, 255, 255, 0.2) 100%
  );
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
  transition: opacity 0.45s ease;
}

/* Tablet : Y plus court, même spread que mobile. */
.tile[data-type="tablet"] .tile-frame {
  --glow-spread: -120px;
  --glow-y: 80px;
}




.tile-inner {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: var(--tile-radius-inner, 20px);
  cursor: none;
  overflow: hidden;
  isolation: isolate;
}



.tile-inner::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(
    circle at var(--gx, 50%) var(--gy, 50%),
    rgba(255, 255, 255, 0.22) 0%,
    rgba(255, 255, 255, 0.05) 35%,
    rgba(255, 255, 255, 0) 65%
  );
  mix-blend-mode: hard-light;
  opacity: 0;
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1);
  pointer-events: none;
  z-index: 2;
}

.tile-inner:hover::after {
  opacity: 1;
}

.tile-scroll {
  width: 100%;
  height: 100%;
  /* `auto` → la scrollbar native (cachée) n'apparaît que si le contenu déborde.
     La scrollbar visible est custom (.tile-scrollbar), gérée en JS. */
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  border-radius: inherit;
  clip-path: inset(0 round var(--tile-radius-inner, 20px));
  /* Transition sur l'élément de base → fade dans les DEUX sens (assombrissement ET retour) */
  transition: filter 0.45s ease;
}

.tile-scroll::-webkit-scrollbar {
  width: 0;
  height: 0;
  background: transparent;
}

/* Scrollbar custom : ligne verticale 1 px, blanc 50 %, hauteur du frame -50 px haut / -50 px
   bas (total -100 px). Par-dessus, un .__fill blanc 100 % grandit de 0 → 100 % avec la
   progression du scroll. `.is-active` est ajoutée en JS quand le contenu déborde ; la
   visibilité finale n'est déclenchée qu'au hover de la tile → fade in/out fluide. */
.tile-scrollbar {
  position: absolute;
  right: 8px;
  top: 50px;
  bottom: 50px;
  width: 1px;
  background: rgba(255, 255, 255, 0.5);
  pointer-events: none;
  z-index: 3;
  opacity: 0;
  transition: opacity 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.tile:hover .tile-scrollbar.is-active {
  opacity: 1;
}

.tile-scrollbar__fill {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 0;
  background: #fff;
}


.tile-content {
  position: relative;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.2);
  font-size: 24px;
  font-weight: 500;
}

.tile-content--image {
  display: block;
}

.tile-content--image img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
}

/* Méta sous chaque tuile : sous-titre + description, largeur fixe = 1 colonne.
   Le bloc lui-même est toujours présent ; ce sont les LIGNES de texte qui apparaissent
   au survol (ou au clic du projet), avec un mouvement bas→haut + fondu, staggered. */
.tile-meta {
  position: absolute;
  top: 100%;
  left: 0;
  padding-top: 16px;
  color: rgba(255, 255, 255, 0.85);
  font-family: 'Suisse Int\'l', 'Suisse Intl', 'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  pointer-events: none;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Lift léger de la meta au rollover (synchro avec le lift de la tile en JS). */
@media (hover: hover) {
  .tile:hover .tile-meta {
    transform: translateY(-8px);
  }
}

/* Wrappers générés par splitIntoLines() : chaque ligne est un .tile-meta__line
   contenant un .tile-meta__line-inner translaté en Y (sans clipping → la ligne
   flotte vers sa position au reveal, plutôt que glisser depuis un masque). */
.tile-meta__line {
  display: block;
  line-height: inherit;
}

.tile-meta__line-inner {
  display: block;
  transform: translateY(24px);
  opacity: 0;
  transition:
    transform 0.5s cubic-bezier(0.22, 1, 0.36, 1) 0s,
    opacity 0.4s ease 0s;
  will-change: transform, opacity;
}

/* Apparition : chaque ligne remonte de 24px → 0, staggered via --line-delay (défini en JS). */
.tile:hover .tile-meta__line-inner,
.tile--project-focused .tile-meta__line-inner {
  transform: translateY(0);
  opacity: 1;
  transition:
    transform 0.8s cubic-bezier(0.22, 1, 0.36, 1) var(--line-delay, 0s),
    opacity 0.6s ease var(--line-delay, 0s);
}

/* Compositable (opacity vs filter brightness) → pas de repaint, transition GPU pure. */
.tile--project-dimmed .tile-scroll {
  opacity: 0.18;
}

/* Contour atténué quand la tile est dimmed (focus sur un autre projet). */
.tile--project-dimmed .tile-frame::before {
  opacity: 0.2;
}

.tile-meta__subtitle {
  font-size: 12px;
  letter-spacing: 0.04em;
  opacity: 0.85;
  margin: 10px 0 0;
}

.tile-meta__desc {
  font-size: 13px;
  line-height: 1.4;
  margin: 12px 0 45px;
  color: rgba(255, 255, 255, 0.95);
}

/* Apparition fondue : opacity 0 au départ, 1 quand l'image a chargé (classe ajoutée en JS).
   Transition combinée avec filter (utilisée pour le fondu du flou au déverrouillage). */
.tile-content--image img {
  opacity: 0;
  transition: opacity 0.5s cubic-bezier(0.22, 1, 0.36, 1),
              filter 0.9s cubic-bezier(0.4, 0, 0.2, 1);
}

.tile-content--image img.is-loaded {
  opacity: 1;
}

.tile-img--locked {
  filter: blur(24px);
}

/* Overlay noir 20% par-dessus l'image floutée, sous le cadenas/input.
   Fondu au déverrouillage. */
.tile-overlay-locked {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.2);
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  transition: opacity 0.9s cubic-bezier(0.4, 0, 0.2, 1);
}

.tile-lock {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 17px;
  height: 22px;
  color: rgba(255, 255, 255, 0.9);
  z-index: 5;
  cursor: pointer;
  pointer-events: auto;
  transition: opacity 0.6s ease, color 0.3s ease, transform 0.3s ease;
}

.tile-lock:hover {
  color: #fff;
  transform: translate(-50%, -50%) scale(1.1);
}

/* Tactile (hover absent ou pointeur grossier = doigt) : restaure le curseur natif et
   masque le curseur custom (le JS, lui, skip déjà tilt/trail/auto-scroll dans attachTilt
   et attachScroll). Empêche aussi le curseur invisible (cursor: none) qui dérange certains
   stylets / écrans tactiles laptop. */
@media (hover: none), (pointer: coarse) {
  html, body,
  .tile-inner { cursor: auto; }
  #cursor { display: none; }
}

/* Accessibilité : prefers-reduced-motion. Neutralise toutes les animations longues,
   transitions et l'auto-scroll. Le glow reste visible au hover (statique, sans pulse).
   Les lignes de meta sont visibles instantanément (pas de slide-up). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .tile-meta__line-inner {
    transform: none !important;
    opacity: 1 !important;
  }
  .tile:hover {
    --glow-alpha: 1;
  }
}

/* Input sans habillage : pas de fond, pas de bord. Le caret blanc reste visible
   (clignote pour signaler la saisie), et les puces du type=password matérialisent
   les caractères tapés. */
.tile-pw {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 6;
  background: transparent;
  color: rgba(255, 255, 255, 0.95);
  caret-color: rgba(255, 255, 255, 0.95);
  border: none;
  padding: 0;
  font-family: inherit;
  font-size: 16px;
  letter-spacing: 0.15em;
  text-align: center;
  outline: none;
  width: 110px;
  height: 28px;
}

