/* ══ Accueil : « Par où commencer ? » en grille projecteur (design Site v3) ═══
   Feuille DE PAGE (chargée par landing.html seulement, patron de methode.css) :
   le contrat de poids de l'accueil compte style.css, et le bloc de la cascade
   de /apps n'a rien à y faire ; symétriquement celui-ci n'a rien à faire sur
   /apps. Six cadres photo au format 2,05:1, trois par rangée. Le script
   static/js/spotlight.js met en lumière le cadre le plus proche du centre de
   l'écran (`.is-on`), les autres passent en gris et en retrait, et la
   description du cadre en lumière s'écrit sous la grille. Deux écarts assumés
   par rapport à la maquette, pour le visiteur qui arrive : le libellé reste
   lisible sur TOUS les cadres (le projecteur hiérarchise, il ne cache pas), et
   un clic navigue toujours. Sans script : tout en couleur, pas de description
   sous la grille (elle ne serait jamais remplie). */
/* La grille est plus large que les autres sections (1240 px contre 1080) : à
   1080, trois cadres par rangée tombent à 327 px et leurs captures ne se
   lisent plus. Le même seuil commande les ruptures : deux par rangée sous
   1120 px (à 1121 px un cadre fait 345 px ; à 1024, le portable le plus
   courant, trois par rangée en faisaient 309), un par rangée sous 700 px (à
   701 px, 321 px). Le bloc-titre garde sa mesure de 640 px. */
.home-start { max-width: 1240px; }
.spot-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
.spot-tile {
  position: relative;
  display: block;
  flex: 0 0 calc((100% - 20px) / 3);
  min-width: 0;
  aspect-ratio: 2.05 / 1;
  border-radius: 14px;
  overflow: hidden;
  background: var(--paper-2);
  color: #fff;
  text-decoration: none;
  transition: transform 0.55s var(--ease-spring);
}
/* L'ombre du cadre en lumière est un calque à part dont seule l'OPACITÉ
   s'anime : une transition de box-shadow repeint le cadre et ses 80 px de
   flou à chaque image (mesuré : 936 repeints sur 61 images de défilement),
   l'opacité tourne sur le compositeur. Hors de la boîte rognée du cadre,
   sinon l'ombre ne dépasserait pas de lui : c'est le parent qui la porte. */
.spot-tile::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transition: opacity 0.55s var(--ease-standard);
  pointer-events: none;
}
.spot-tile picture,
.spot-tile .spot-img { position: absolute; inset: 0; width: 100%; height: 100%; }
.spot-tile .spot-img {
  display: block;
  object-fit: cover;
  object-position: top center;
  transition: transform 0.7s var(--ease-standard);
}
/* Le zoom au survol sous un garde (hover: hover) : sur un appareil tactile
   iOS garde l'état de survol jusqu'au tap suivant (règle maison). */
@media (hover: hover) {
  .spot-tile:hover .spot-img { transform: scale(1.03); }
}
/* Cadre sans capture : aplat d'accent assombri, même mélange que les tuiles
   d'app (70 % d'accent : c'est la clarté qui décide de la lisibilité du
   blanc), trame de points et picto en filigrane. */
.spot-panel {
  position: absolute; inset: 0;
  background: color-mix(in oklch, var(--spot-accent, var(--accent-azur)) 70%, #16140f);
}
.spot-panel::before {
  content: ""; position: absolute; inset: 0;
  background-image: radial-gradient(circle, rgba(255, 255, 255, 0.14) 1px, transparent 1.4px);
  background-size: 16px 16px;
  -webkit-mask-image: linear-gradient(135deg, #000 0%, transparent 62%);
  mask-image: linear-gradient(135deg, #000 0%, transparent 62%);
}
.spot-panel svg {
  position: absolute; right: 8%; top: 50%;
  width: 34%; height: 60%;
  transform: translateY(-50%);
  opacity: 0.22;
}
/* Le voile est ce qui porte le libellé : mesuré au navigateur sur les pixels
   réellement peints, à 0,66 d'opacité en bas le blanc tombait à 3,0:1 sur
   les captures claires (le cadre du cahier des charges, une page presque
   blanche). Le bas du voile est donc plus dense et remonte plus haut, la
   moitié supérieure de la capture reste dégagée. */
.spot-veil {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.82) 0%, rgba(0, 0, 0, 0.66) 26%,
                              rgba(0, 0, 0, 0.3) 48%, rgba(0, 0, 0, 0) 70%);
  pointer-events: none;
}
.spot-cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 12px 14px;
  display: flex; justify-content: space-between; align-items: flex-end; gap: 12px;
  font-size: 12.5px; line-height: 1.25;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.55);
}
.spot-cap-txt { min-width: 0; }
.spot-title {
  display: block; margin: 0;
  font-family: var(--font-display);
  font-size: 15px; font-weight: 700; letter-spacing: -0.01em;
  color: inherit;
}
.spot-sub { font-weight: 400; opacity: 0.92; }
.spot-kind {
  flex: none;
  font-family: var(--font-mono);
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  white-space: nowrap;
  background: rgba(255, 255, 255, 0.92); color: #0a0a0a;
  border-radius: 999px; padding: 4px 9px;
  text-shadow: none;
}
/* La description de chaque cadre est aussi dans son lien, hors écran : le
   lecteur d'écran l'entend avec le nom, le paragraphe sous la grille n'a pas
   besoin d'être annoncé à chaque défilement. */
.spot-sr {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Projecteur (script armé). Le fondu ne porte que sur l'IMAGE (et l'aplat
   de repli), jamais sur le cadre entier : la maquette éteignait tout le
   cadre, libellé compris, et un cadre éteint ne disait plus ce qu'il ouvre.
   Le voile sombre du bas reste opaque, donc le libellé blanc reste lisible
   sur un cadre en retrait comme sur le cadre en lumière. */
.spot-grid.is-live .spot-tile { transform: scale(0.985); }
.spot-grid.is-live .spot-tile .spot-img,
.spot-grid.is-live .spot-tile .spot-panel {
  opacity: 0.5;
  filter: grayscale(1) contrast(0.78) brightness(1.06);
}
.spot-grid.is-live .spot-tile.is-on { transform: none; }
.spot-grid.is-live .spot-tile.is-on::after { opacity: 1; }
.spot-grid.is-live .spot-tile.is-on .spot-img,
.spot-grid.is-live .spot-tile.is-on .spot-panel,
.spot-grid.is-live .spot-tile:focus-visible .spot-img,
.spot-grid.is-live .spot-tile:focus-visible .spot-panel { opacity: 1; filter: none; }
.spot-tile:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }
.spot-grid.is-live .spot-tile .spot-img,
.spot-grid.is-live .spot-tile .spot-panel {
  transition: opacity 0.55s var(--ease-standard), filter 0.55s var(--ease-standard),
              transform 0.7s var(--ease-standard);
}
/* En thème sombre, un cadre « éteint » s'assombrit plutôt que de blanchir :
   un gris clair sur le papier sombre ferait ressortir les cadres en retrait
   plus que celui en lumière. */
[data-theme="dark"] .spot-grid.is-live .spot-tile .spot-img,
[data-theme="dark"] .spot-grid.is-live .spot-tile .spot-panel {
  opacity: 0.55;
  filter: grayscale(1) contrast(0.85) brightness(0.75);
}
[data-theme="dark"] .spot-grid.is-live .spot-tile.is-on .spot-img,
[data-theme="dark"] .spot-grid.is-live .spot-tile.is-on .spot-panel,
[data-theme="dark"] .spot-grid.is-live .spot-tile:focus-visible .spot-img,
[data-theme="dark"] .spot-grid.is-live .spot-tile:focus-visible .spot-panel { opacity: 1; filter: none; }
.spot-desc {
  margin: 22px auto 0;
  max-width: 640px;
  text-align: center;
  font-size: 15px; line-height: 1.5;
  color: var(--muted);
  min-height: 46px;
  text-wrap: pretty;
}
/* Sans script, le paragraphe reste vide : il n'existe pas. En reduced-motion la
   grille n'est pas armée non plus, mais le survol et le focus écrivent la
   description : c'est donc le VIDE qui la cache, pas l'absence du script.
   Frère GÉNÉRAL et non adjacent : les pastilles du rail se sont posées entre
   la grille et le paragraphe, et un `+` a cessé de le désigner. */
.spot-grid:not(.is-live) ~ .spot-desc:empty { display: none; }
/* Les pastilles ne sont posées que sur le rail : ailleurs le conteneur est
   vide, et une boîte vide ne doit pas prendre sa marge. */
.spot-dots:empty { display: none; }
@media (max-width: 1120px) {
  .spot-tile { flex-basis: calc((100% - 10px) / 2); }
}
/* L'indice et les pastilles n'existent que sur le rail (téléphone). */
.spot-rail-hint, .spot-dots { display: none; }
/* ── Téléphone : la grille devient un RAIL qui se fait glisser ───────────────
   Empilés à 100 %, les six cadres faisaient 1509 px de haut sur un écran de
   390 (mesuré), soit près de deux écrans pour une seule section, et le
   projecteur s'y retournait contre lui : on voit deux cadres à la fois, donc
   à tout instant un cadre en lumière et un cadre en gris — sur un ordinateur
   la rangée de trois fait lire l'effet comme une hiérarchie, sur un téléphone
   il fait lire la moitié de l'écran comme une image qui n'a pas fini de
   charger. Le rail rend les six en un seul écran (~230 px), garde la mise en
   lumière (celle du cadre au centre du rail) et donne au geste le sens qu'il
   a : on feuillette des portes. C'est la doctrine déjà retenue pour la
   cascade de /apps sous 721 px, appliquée ici.
   `scroll-snap-align: start` plutôt que `center` : un cadre à 84 % laisse
   toujours voir la tranche du suivant, qui est l'affordance du rail, et le
   PREMIER cadre est atteignable (centré, il aurait fallu 31 px de garniture
   pour 20 px de gouttière). */
@media (max-width: 700px) {
  .spot-grid {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline-start: 20px;
    -webkit-overflow-scrolling: touch;
    /* Bord à bord : les marges négatives valent la gouttière de .home-section
       à cette largeur (20 px), la garniture la rend au contenu. */
    margin-inline: -20px;
    padding: 4px 20px 10px;
    gap: 12px;
    scrollbar-width: none;
  }
  .spot-grid::-webkit-scrollbar { display: none; }
  .spot-tile {
    flex: 0 0 84%;
    scroll-snap-align: start;
    /* Plus haut que sur ordinateur : à 2,05:1 un cadre de 328 px n'a que
       160 px de haut, dont 40 de libellé — la capture n'y disait plus rien. */
    aspect-ratio: 1.5 / 1;
  }
  .spot-grid.is-live .spot-tile { transform: none; }
  .spot-cap { padding: 10px 12px; }
  .spot-rail-hint {
    display: block;
    margin: 0 0 10px;
    font-family: var(--font-mono);
    font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--muted-3);
  }
  /* Les pastilles : où on en est dans le rail, et combien il en reste. */
  .spot-dots {
    display: flex; justify-content: center; gap: 6px;
    margin-top: 12px;
  }
  .spot-dots i {
    display: block; width: 6px; height: 6px; border-radius: 999px;
    background: var(--line-strong);
    transition: width 0.3s var(--ease-standard), background 0.3s var(--ease-standard);
  }
  .spot-dots i.is-on { width: 20px; background: var(--fg); }
  /* La description ne colle plus au bas de l'écran : la section tient
     désormais dans un écran, le paragraphe est sous le rail, à sa place. Sa
     hauteur est RÉSERVÉE (les six textes ne font pas la même longueur) sinon
     la page sautait sous le doigt à chaque cadre feuilleté. */
  .spot-desc {
    margin-top: 14px;
    font-size: 14px;
    min-height: 105px;
    text-align: left;
  }
}
/* Sur le plus étroit des téléphones encore en service (iPhone SE de première
   génération, 320 px), deux des six textes passent à huit lignes : la réserve
   de 105 px ne suffit plus et la page sautait de 21 px sous le doigt à chaque
   cadre feuilleté. Mesuré cadre par cadre, aux trois largeurs. */
@media (max-width: 345px) {
  .spot-desc { min-height: 126px; }
}
@media (prefers-reduced-motion: reduce) and (max-width: 700px) {
  .spot-grid { scroll-behavior: auto; }
  .spot-dots i { transition: none; }
}
@media (prefers-reduced-motion: reduce) {
  .spot-tile, .spot-tile::after, .spot-tile .spot-img, .spot-tile .spot-panel { transition: none; }
  .spot-tile:hover .spot-img { transform: none; }
  /* Le cadre survolé ou focalisé se signale sans mouvement : une ombre. */
  .spot-tile.is-on::after { opacity: 1; }
}
