/* Tuile : une boîte de quatre faces (avant, arrière, droite, gauche).
   Les dimensions W, H, D sont posées par js/grid.js dans --w, --h, --d.
   Jamais de box-shadow ni de filter sur une face. */

.tile {
  position: relative;
  margin: 0;
  padding: 0;
  transform-style: preserve-3d;
}

/* Chaque flanc porte le même trait fin (--edge) : le contour de la boîte
   reste lisible quelle que soit la face tournée vers le spectateur, sans que
   les arêtes se doublent. Les faces sont éclairées du haut à gauche (voir les
   jetons --surface-top, --surface-dark, --surface-under) : le volume se lit
   par la matière, le trait ne fait que le cerner. */
.face {
  position: absolute;
  inset: 0;
  border: 1px solid var(--edge);
  border-radius: var(--radius-tile);
}

/* Face avant : W × H, avancée de D/2. Porte le contenu éditorial, un filet
   un peu plus marqué et un liseré clair sur son arête haute. */
.face--front {
  background: var(--surface-face);
  color: var(--text-primary);
  border-color: var(--border);
  overflow: hidden;
  /* Pendant une roulade, Chrome ne coupe plus les coins arrondis d'une face
     en 3D : une image plein cadre montre ses angles droits (blancs) hors du
     galbe. clip-path, lui, suit la couche composée. */
  clip-path: inset(0 round var(--radius-tile));
  transform: translateZ(calc(var(--d) / 2));
}

.face--front::after {
  content: "";
  position: absolute;
  inset: 0;
  border-top: 1px solid var(--edge-light);
  border-radius: calc(var(--radius-tile) - 1px);
  pointer-events: none;
}

/* Face arrière : W × H, retournée. */
.face--back {
  background: var(--surface-side);
  transform: rotateY(180deg) translateZ(calc(var(--d) / 2));
}

/* Faces latérales : D × H, centrées puis poussées à ±W/2. La droite, dans
   l'ombre, est plus sombre que la gauche. */
.face--right,
.face--left {
  right: auto;
  width: var(--d);
  left: calc((var(--w) - var(--d)) / 2);
  background: var(--surface-side);
}

.face--right {
  background: var(--surface-dark);
  transform: rotateY(90deg) translateZ(calc(var(--w) / 2));
}

.face--left {
  transform: rotateY(-90deg) translateZ(calc(var(--w) / 2));
}

/* Faces haute et basse : W × D, centrées puis poussées à ±H/2. Créées par
   grid.js seulement si CAP_FACES est vrai : la vue oblique de la scène montre
   le dessus de chaque boîte, éclairé ; le dessous est le plus sombre. */
.face--top,
.face--bottom {
  bottom: auto;
  height: var(--d);
  top: calc((var(--h) - var(--d)) / 2);
}

.face--top {
  background: var(--surface-top);
  transform: rotateX(90deg) translateZ(calc(var(--h) / 2));
}

.face--bottom {
  background: var(--surface-under);
  transform: rotateX(-90deg) translateZ(calc(var(--h) / 2));
}

/* Contenu de la face avant ------------------------------------------- */

.tile__link,
.tile__inner {
  position: absolute;
  inset: 0;
  display: flex;
  color: inherit;
  text-decoration: none;
}

.tile__link:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -4px;
  border-radius: var(--radius-tile);
}

.tile__img {
  display: block;
  flex: 0 0 50%;
  min-width: 0;
  min-height: 0;
  object-fit: cover;
}

.tile__body {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  /* Un texte trop long pour sa case n'en sort jamais : sinon, quand l'image
     vient après lui (image en bas, bandeau mobile), elle le recouvre. */
  overflow: hidden;
}

.tile__title {
  margin: 0;
  font-size: var(--fs-l);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  overflow-wrap: anywhere;
}

.tile__title--quote {
  font-weight: var(--fw-regular);
  font-style: italic;
}

.tile__title--quote p {
  margin: 0;
}

.tile__title--quote p::before {
  content: "« ";
}

.tile__title--quote p::after {
  content: " »";
}

.tile__excerpt {
  margin: 0;
  font-size: var(--fs-m);
  color: var(--text-muted);
}

.tile__excerpt cite {
  font-style: normal;
}

.tile__meta {
  margin: auto 0 0;
  font-size: var(--fs-s);
  color: var(--text-muted);
}

/* Formats ------------------------------------------------------------- */

/* 1×1 : image plein cadre, sans texte. */
.tile--1x1 .tile__img {
  flex: 1 1 auto;
  width: 100%;
  height: 100%;
}

/* 1×1 sur une colonne (mobile) : la couverture en bandeau sur le haut, le
   texte dessous, l'extrait tenu à deux lignes (posé par grid.js, span.band). */
.tile--band .tile__link,
.tile--band .tile__inner {
  flex-direction: column;
}

.tile--1x1.tile--band .tile__img {
  flex: 0 0 44%;
  width: 100%;
  height: 44%;
}

/* Titre et extrait tenus à quelques lignes quand la case est étroite (1×1,
   colonne de texte d'un 1×2 ou d'un bandeau mobile) : la date reste visible. */
.tile--1x1 .tile__excerpt,
.tile--1x2 .tile__excerpt,
.tile--1x1 .tile__title,
.tile--1x2 .tile__title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

.tile--band .tile__excerpt {
  -webkit-line-clamp: 2;
}

.tile--band .tile__title {
  -webkit-line-clamp: 3;
}

/* Les blocs de texte gardent leur hauteur (pas de ligne coupée en deux) ;
   ce qui ne tient pas est simplement caché en bas de la case. */
.tile__title,
.tile__excerpt,
.tile__meta,
.tile__caption,
.tile__count {
  flex-shrink: 0;
}

/* Bandeau mobile sur un écran étroit : la case est trop basse pour l'image,
   trois lignes de titre, l'extrait et la date à la fois. L'extrait s'efface
   en premier, puis le titre se tient à deux lignes. */
@media (max-width: 439.98px) {
  .tile--band .tile__excerpt {
    display: none;
  }
}

@media (max-width: 359.98px) {
  .tile--band .tile__title {
    -webkit-line-clamp: 2;
  }
}

/* 2×1 : image à gauche, moitié de la tuile. */
.tile--2x1 .tile__link,
.tile--2x1 .tile__inner {
  flex-direction: row;
}

.tile--2x1 .tile__img {
  width: 50%;
  height: 100%;
}

/* 1×2 : image en haut, moitié de la tuile. */
.tile--1x2 .tile__link,
.tile--1x2 .tile__inner {
  flex-direction: column;
}

/* Image à la fin (imagePos: "end") : à droite en 2×1, en bas en 1×2 et sur
   le bandeau mobile. */
.tile--2x1.tile--image-end .tile__link,
.tile--2x1.tile--image-end .tile__inner {
  flex-direction: row-reverse;
}

.tile--1x2.tile--image-end .tile__link,
.tile--1x2.tile--image-end .tile__inner,
.tile--band.tile--image-end .tile__link,
.tile--band.tile--image-end .tile__inner {
  flex-direction: column-reverse;
}

.tile--1x2 .tile__img {
  width: 100%;
  height: 50%;
}

/* 3×1 et 4×1 : les bandes larges (présentation, news du jour). Une image
   occupe un carré à gauche, le texte le reste. Sur deux colonnes la bande
   redevient un 2×1 ordinaire, sur une un bandeau mobile. */
.tile--3x1 .tile__link,
.tile--3x1 .tile__inner,
.tile--4x1 .tile__link,
.tile--4x1 .tile__inner {
  flex-direction: row;
}

.tile--3x1 .tile__img {
  flex-basis: 33.333%;
  width: 33.333%;
  height: 100%;
}

.tile--4x1 .tile__img {
  flex-basis: 25%;
  width: 25%;
  height: 100%;
}

.tile--3x1 .tile__body,
.tile--4x1 .tile__body {
  padding: 20px 24px;
}

.tile--3x1 .tile__title,
.tile--4x1 .tile__title {
  font-size: calc(var(--fs-l) * 1.3);
}

/* Icône au-dessus du titre (logo d'un lien) : un carré de 40 px. */
.tile__icon {
  display: block;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  object-fit: contain;
}

/* News du jour : la bande 4×1 d'un billet mis en avant. L'image prend la
   moitié, la légende « News du jour » passe en ambre, comme tout ce qui est
   actif ; la date reste à côté. */
.tile--4x1.tile--featured .tile__img {
  flex-basis: 50%;
  width: 50%;
}

.tile--featured .tile__caption {
  margin: auto 0 0;
  font: var(--fw-medium) var(--fs-s) / 1.2 var(--font-mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent);
}

.tile--featured .tile__meta {
  margin-top: 0;
}

/* Présentation : le nom, la ligne de rôle (légende) juste dessous, puis le
   texte, en clair et non en gris, c'est le propos de la page. */
.tile--profile .tile__caption {
  order: 0;
  margin: 0;
}

.tile--profile .tile__excerpt {
  order: 1;
  color: var(--text-primary);
}

.tile--profile .tile__meta {
  order: 2;
}

/* Variantes de contenu ------------------------------------------------ */

/* Note : une boîte relevée, plus claire que les autres, jamais ambre. */
.tile--note .face--front,
.tile--active .face--front {
  background: var(--surface-lift);
  color: var(--text-primary);
}

.tile--note .face--top,
.tile--active .face--top {
  background: var(--surface-lift-top);
}

.tile--note .face--left,
.tile--note .face--back,
.tile--active .face--left,
.tile--active .face--back {
  background: var(--surface-lift-side);
}

.tile--note .face--right,
.tile--active .face--right {
  background: var(--surface-lift-dark);
}

.tile--note .tile__excerpt,
.tile--note .tile__meta {
  color: var(--text-on-lift-muted);
}

.tile--link .tile__title::after {
  content: " ↗";
  font-weight: var(--fw-regular);
  color: var(--text-muted);
}

/* Page Playlist : cubes de genres et de sous-genres. Une légende courte sous
   le titre, toujours affichée (même en 1×1) ; le sous-genre choisi est une
   boîte relevée cerclée de clair ; une case sans playlist est en pointillé et
   muette. */
.tile__caption {
  margin: auto 0 0;
  font-size: var(--fs-s);
  color: var(--text-muted);
}

/* Compte (sous-genres d'un genre, titres d'une liste) : le chiffre en grand,
   en mono, le libellé en petites capitales dessous, comme la barre. C'est le
   motif de la face, là où la légende laissait les trois quarts nus. */
.tile__count {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: auto 0 0;
  line-height: 1;
}

.tile__count-value {
  font: var(--fw-medium) calc(var(--fs-l) * 2) / 1 var(--font-mono);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.tile__count-label {
  font: var(--fw-medium) var(--fs-s) / 1.2 var(--font-mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.tile--genre .tile__title,
.tile--subgenre .tile__title,
.tile--list .tile__title {
  font-size: calc(var(--fs-l) * 1.2);
}

/* Un cube de genre musical est indigo, ses sous-genres restent ardoise : la
   grille dit d'un coup d'œil à quel niveau on se trouve. Même éclairage que
   les autres boîtes. Les cases à remplir (« Nouveau genre ») gardent leur
   pointillé. */
.tile--genre:not(.tile--empty) .face--front {
  background: var(--surface-genre);
  color: var(--text-on-genre);
}

.tile--genre:not(.tile--empty) .face--left,
.tile--genre:not(.tile--empty) .face--back {
  background: var(--surface-genre-side);
}

.tile--genre:not(.tile--empty) .face--top {
  background: var(--surface-genre-top);
}

.tile--genre:not(.tile--empty) .face--right {
  background: var(--surface-genre-dark);
}

.tile--genre:not(.tile--empty) .face--bottom {
  background: var(--surface-genre-under);
}

.tile--genre:not(.tile--empty) .tile__caption,
.tile--genre:not(.tile--empty) .tile__count-label {
  color: var(--text-on-genre);
}

.tile--genre:not(.tile--empty) .tile__count-label {
  opacity: .8;
}

.tile--genre:not(.tile--empty) .tile__link:focus-visible {
  outline-color: var(--text-on-genre);
}

/* Tuile-bouton (« Nouvelle liste ») : même rendu qu'un lien de tuile ; son
   contenu est fait de span, affichés comme les blocs qu'ils remplacent. */
.tile__link--button {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.tile__link--button .tile__title,
.tile__link--button .tile__caption {
  display: block;
}

/* Cube choisi : la boîte relevée (voir .tile--note) cerclée d'un cadre clair
   de 2 px ; le liseré haut s'efface derrière le cadre. */
.tile--active .face--front {
  border: 2px solid var(--outline-active);
}

.tile--active .face--front::after {
  display: none;
}

.tile--active .tile__caption,
.tile--active .tile__count-label {
  color: var(--text-on-lift-muted);
}

/* Case à remplir (nouvelle liste, sous-genre sans playlist) : filet en
   points plus gros et plus aérés que le `dotted` du navigateur. Le volume
   reste peint ; le motif est un masque, pas un box-shadow ni un filter. */
.tile--empty {
  --dot: 2.25px;
  --dot-step: 16px;
}

.tile--empty .face {
  border-color: transparent;
}

.tile--empty .face--front {
  background: var(--surface-face);
}

/* Le motif en points remplace le liseré haut de la face avant. */
.tile--empty .face--front::after {
  border-top: 0;
}

.tile--empty .face::after {
  content: "";
  position: absolute;
  inset: 2px;
  border-radius: calc(var(--radius-tile) - 2px);
  padding: calc(var(--dot) * 2);
  pointer-events: none;
  background: radial-gradient(circle, var(--border) var(--dot), transparent calc(var(--dot) + 0.4px))
    0 0 / var(--dot-step) var(--dot-step);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.tile--empty .tile__title {
  color: var(--text-muted);
}

/* États --------------------------------------------------------------- */

/* Tuile à plat (couche FLIP) : seule la face avant reste visible. */
.tile--flat {
  transform-style: flat;
}

.tile--flat .face--front {
  transform: none;
}

.tile--flat .face:not(.face--front) {
  display: none;
}

/* En attente de roulade d'entrée : présente dans la grille, invisible. */
.tile--pending {
  visibility: hidden;
}

/* Fantôme : cible d'un FLIP de retour, révélée à la fin. L'opacité est posée
   sur les faces, jamais sur la tuile (elle aplatirait la 3D). */
.tile--ghost .face {
  opacity: 0;
}

.tile--reveal .face {
  transition: opacity 150ms linear;
}

/* Fondus de remplacement (reduced-motion, tuiles en surnombre). */
.tile--fade-out .face {
  animation: tile-fade-out var(--dur-fade) linear var(--fade-delay, 0ms) both;
}

.tile--fade-in .face {
  animation: tile-fade-in var(--dur-fade) linear var(--fade-delay, 0ms) both;
}

@keyframes tile-fade-out {
  from { opacity: 1; }
  to { opacity: 0; }
}

@keyframes tile-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
