/* ──────────────────────────────────────────────────────────────────────────
   crt.css — Filtre écran cathodique (CRT) global, multi-couches.

   Philosophie : un vrai CRT n'est PAS une grosse texture posée dessus, c'est
   une alchimie de plusieurs couches FAIBLES qui se mélangent au contenu via
   des blend modes. Valeurs calibrées d'après le shader crt-fx (scanlines douces
   ~0.5, vignette ~0.3, bruit ~0.05, flicker ~0.03, masque phosphore ~0.5,
   aberration ~2.5px, barrel ~0.02).

   ── Architecture critique (mix-blend-mode) ──
   Chaque couche est un enfant DIRECT de <body> (pas de wrapper) : un conteneur
   position:fixed créerait un stacking context isolé et les blend modes ne se
   mélangeraient qu'entre couches, pas avec le jeu. En enfants de body (root
   stacking context), multiply/screen agissent vraiment sur le contenu derrière.

   ── Intensité ──
   `--crt-amount` (0..1, posé par crt.js depuis le slider) multiplie l'opacité
   de chaque couche → fondu progressif jusqu'à 0. `html.crt-on` (amount>0) sort
   les couches du display:none (zéro coût quand éteint).

   ── Déformation/aberration réelles ──
   Le split RVB par pixel + le barrel géométrique ne sont pas faisables en DOM
   sans un filtre SVG sur le contenu (cf. .crt-warp ci-dessous + crt.js).
   ────────────────────────────────────────────────────────────────────────── */

:root {
  --crt-amount: 0;               /* maître 0..1, posé par crt.js               */

  --crt-cell: 3px;               /* largeur d'une triade phosphore (« pixels ») */
  --crt-scan-period: 4px;        /* hauteur d'un cycle de scanline             */

  /* Forces de base de chaque couche (× --crt-amount). Calées par l'user via
     crt-debug.js (2026-06-21).                                                */
  --crt-scan-strength:     0.30; /* scanlines (multiply, profil doux)          */
  --crt-mask-strength:     0.30; /* masque phosphore RVB (screen)              */
  --crt-grain-strength:    0.20; /* grain animé (overlay)                      */
  --crt-vignette-strength: 1.00; /* vignette + bombé de tube (normal, sombre)  */
  --crt-aberr-strength:    0.56; /* frange chromatique de bord (screen)        */
  --crt-roll-strength:     0.17; /* barre de balayage qui descend (screen)     */
  --crt-bloom-strength:    0.10; /* halo lumineux central (screen)             */
}

/* Couche générique : plein écran, non interactive, tout en haut de la pile.
   Même z-index pour toutes → l'ordre de peinture suit l'ordre DOM (crt.js les
   insère dans l'ordre voulu). display:none tant que le filtre est éteint. */
.crt-layer {
  position: fixed;
  inset: 0;
  z-index: 2147483646;
  pointer-events: none;
  display: none;
  /* léger lissage des bandes sur écrans haute densité */
  image-rendering: auto;
}
html.crt-on .crt-layer { display: block; }

/* ── 1. Frange chromatique de bord (aberration perçue) ──────────────────────
   Rouge sur un bord, cyan sur l'autre : c'est là que l'aberration d'un tube
   est physiquement la plus visible. Screen = ajoute de la lumière colorée. */
.crt-l-aberration {
  background-image:
    linear-gradient( 90deg, rgba(255, 45, 85, 0.85) 0,  rgba(255, 45, 85, 0) 3.5%),
    linear-gradient(270deg, rgba(45, 200, 255, 0.85) 0, rgba(45, 200, 255, 0) 3.5%),
    linear-gradient(  0deg, rgba(255, 45, 85, 0.50) 0,  rgba(255, 45, 85, 0) 3%),
    linear-gradient(180deg, rgba(45, 200, 255, 0.50) 0, rgba(45, 200, 255, 0) 3%);
  mix-blend-mode: screen;
  opacity: calc(var(--crt-aberr-strength) * var(--crt-amount));
}

/* ── 2. Masque phosphore (les « petits pixels ») ────────────────────────────
   Grille d'ouverture verticale R/V/B (type Trinitron). Screen = phosphores qui
   s'allument. Combiné aux scanlines (gaps horizontaux), ça forme des cellules. */
.crt-l-mask {
  background-image: repeating-linear-gradient(
    90deg,
    rgba(255, 0, 0, 0.55) 0,
    rgba(0, 255, 0, 0.55) calc(var(--crt-cell) / 3),
    rgba(0, 0, 255, 0.55) calc(var(--crt-cell) / 3 * 2),
    rgba(255, 0, 0, 0.55) var(--crt-cell)
  );
  mix-blend-mode: screen;
  opacity: calc(var(--crt-mask-strength) * var(--crt-amount));
}

/* ── 3. Scanlines (profil doux, pas des barres noires) ──────────────────────
   Gradient sinusoïdal-like sur la période : pic sombre au milieu, fondu aux
   bords. Multiply = assombrit le contenu là où passe la ligne. */
.crt-l-scan {
  background-image: repeating-linear-gradient(
    0deg,
    rgba(0, 0, 0, 0) 0,
    rgba(0, 0, 0, 0.65) calc(var(--crt-scan-period) / 2),
    rgba(0, 0, 0, 0) var(--crt-scan-period)
  );
  mix-blend-mode: multiply;
  opacity: calc(var(--crt-scan-strength) * var(--crt-amount));
  animation: crt-flicker 0.08s steps(2, end) infinite;
}

/* ── 4. Barre de balayage (refresh du tube) ─────────────────────────────────
   Large bande douce qui descend lentement. Screen = éclaircit au passage. */
.crt-l-roll {
  background-image: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 1) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  height: 28%;
  inset: 0 0 auto 0;            /* ancrée en haut, animée en translateY        */
  mix-blend-mode: screen;
  opacity: calc(var(--crt-roll-strength) * var(--crt-amount));
  transform: translate3d(0, -130%, 0);
  animation: crt-roll 6.5s linear infinite;
  will-change: transform;
}

/* ── 5. Grain animé (bruit de luminance) ────────────────────────────────────
   Tuile de bruit fractal SVG (grayscale), repositionnée par à-coups pour
   « bouillir ». Overlay = module la luminance sans laver les couleurs. */
.crt-l-grain {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
  mix-blend-mode: overlay;
  opacity: calc(var(--crt-grain-strength) * var(--crt-amount));
  animation: crt-grain 0.5s steps(1, end) infinite;
  will-change: background-position;
}

/* ── 6. Vignette + bombé de tube + halo central ─────────────────────────────
   Couche du dessus. Le noir radial assombrit les coins (courbure perçue), le
   box-shadow inset ronge les bords, le halo blanc central donne le verre bombé. */
.crt-l-glow {
  background:
    radial-gradient(ellipse 78% 82% at 50% 44%,
      rgba(255, 255, 255, calc(var(--crt-bloom-strength) * 3)) 0%,
      transparent 55%),
    radial-gradient(ellipse 100% 100% at 50% 50%,
      transparent 52%,
      rgba(0, 0, 0, 0.75) 100%),
    radial-gradient(ellipse 140% 120% at 50% 50%,
      transparent 70%,
      rgba(0, 0, 0, 0.55) 100%);
  box-shadow:
    inset 0 0 90px 12px rgba(0, 0, 0, 0.30),
    inset 0 0 22px rgba(0, 0, 0, 0.22);
  opacity: calc(var(--crt-vignette-strength) * var(--crt-amount));
}

/* ── Animations ─────────────────────────────────────────────────────────── */
@keyframes crt-flicker {
  0%, 100% { opacity: calc(var(--crt-scan-strength) * var(--crt-amount)); }
  50%      { opacity: calc(var(--crt-scan-strength) * var(--crt-amount) * 0.93); }
}
@keyframes crt-roll {
  0%   { transform: translate3d(0, -130%, 0); }
  100% { transform: translate3d(0, 460%, 0); }
}
@keyframes crt-grain {
  0%   { background-position: 0      0; }
  20%  { background-position: -30px  10px; }
  40%  { background-position: 25px  -20px; }
  60%  { background-position: -15px -25px; }
  80%  { background-position: 30px   15px; }
  100% { background-position: 0      0; }
}

/* ── Réel : aberration RVB par pixel + barrel, via filtre SVG sur le contenu ──
   Appliqué à #app-root (cf. crt.js) UNIQUEMENT si activé (perf). La force du
   filtre est pilotée côté SVG (feOffset/feDisplacementMap) par crt.js. */
/* Warp viewport-correct sur TOUTES les pages, NAV BAR COURBÉE COMPRISE.
   On transforme .app en un "tube" FIXE de la taille du viewport (overflow:hidden,
   NE scrolle pas). crt.js déplace le contenu dans un enfant #crt-scroll qui, lui,
   scrolle. La nav bar et les modales internes restent enfants DIRECTS de .app
   (hors de #crt-scroll) → elles sont COURBÉES par le filtre du .app ET ancrées au
   viewport (elles ne scrollent pas). Le barrel reste calé sur le viewport. */
html.crt-warp { overflow-x: clip; }
html.crt-warp body { overflow: hidden; }
html.crt-warp .app {
  position: fixed;
  inset: 0;
  overflow: hidden;               /* le scroll vit dans #crt-scroll */
  padding: 0 !important;          /* padding reporté sur #crt-scroll ; nav full-width */
  /* Fond papier OPAQUE (!important : une règle .app plus spécifique le rendait
     transparent sur certaines pages → la grille body::before transparaissait,
     droite, EN PLUS de la grille warpée → deux quadrillages). */
  background: var(--paper, #FFFDF5) !important;
  filter: url(#crt-warp);
  /* Overscan : le barrel convexe rétrécit le contenu → on agrandit pour
     re-remplir le cadre. Facteur posé par crt.js, proportionnel au barrel. */
  transform: scale(var(--crt-warp-scale, 1));
  transform-origin: center center;
}

/* Quadrillage DANS le .app filtré → il se courbe bord à bord avec le contenu
   (copie de body::before). Figé (pas d'animation) : une grille qui défile sous
   le filtre forcerait un recalcul du barrel à chaque frame. */
html.crt-warp .app::before {
  content: '';
  position: absolute;
  inset: calc(-1 * var(--grid-size, 32px));
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right,  var(--grid-blue, rgba(116, 185, 255, 0.35)) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-blue, rgba(116, 185, 255, 0.35)) 1px, transparent 1px);
  background-size: var(--grid-size, 32px) var(--grid-size, 32px);
  background-repeat: repeat;
}
/* Le contenu scrollé passe AU-DESSUS du quadrillage. */
html.crt-warp #crt-scroll { z-index: 1; }

/* La grille d'origine (body::before, hors du filtre, droite) est MASQUÉE sous
   CRT : seule la copie warpée .app::before reste → un seul quadrillage, courbé. */
html.crt-warp body::before { display: none !important; }

/* Conteneur de scroll interne : c'est lui qui défile, SOUS le filtre du .app. */
html.crt-warp #crt-scroll {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  /* padding contenu (marge latérale pour les box-shadow / titres) + place sous
     la nav bar ancrée en bas. */
  padding: 12px 16px calc(92px + env(safe-area-inset-bottom, 0px)) 16px;
}

/* Nav bar : enfant direct de .app → COURBÉE par le filtre + ancrée au bas du
   viewport (hors de #crt-scroll, donc ne scrolle pas). */
html.crt-warp .mobile-footer-nav.is-fixed {
  position: absolute !important;
  left: 0;
  right: 0;
  bottom: 0;
  filter: none;                   /* le .app la warpe déjà ; pas de filtre dédié */
}

/* Scrollbars masquées sous CRT (déformées par le filtre + reflow → à-coups). */
html.crt-warp #crt-scroll,
html.crt-warp .settings-scroll,
html.crt-warp .changelog-body { scrollbar-width: none; }
html.crt-warp #crt-scroll::-webkit-scrollbar,
html.crt-warp .settings-scroll::-webkit-scrollbar,
html.crt-warp .changelog-body::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* Modales de NIVEAU BODY (settings/feedback/changelog/gameover/confirm injectées
   sur <body>) : hors de .app → on leur applique le warp directement (`body >`
   pour NE PAS double-filtrer les modales internes à .app, déjà warpées). */
html.crt-warp body > .settings-backdrop,
html.crt-warp body > .feedback-backdrop,
html.crt-warp body > .changelog-backdrop,
html.crt-warp body > .gameover-backdrop,
html.crt-warp body > .confirm-backdrop {
  filter: url(#crt-warp);
}

/* SVG de définitions des filtres : présent mais invisible et hors layout. */
.crt-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
  pointer-events: none;
}

/* ── Accessibilité / préférence d'animation ─────────────────────────────────
   On coupe TOUT le mouvement (flicker, balayage, grain) et on masque la barre
   de balayage. La texture statique (scanlines, masque, vignette) reste. */
html.reduce-motion .crt-l-scan,
html.reduce-motion .crt-l-roll,
html.reduce-motion .crt-l-grain,
html.anim-reduced  .crt-l-scan,
html.anim-reduced  .crt-l-roll,
html.anim-reduced  .crt-l-grain { animation: none; }
html.reduce-motion .crt-l-roll,
html.anim-reduced  .crt-l-roll { display: none; }
html.reduce-motion .crt-l-scan,
html.anim-reduced  .crt-l-scan { opacity: calc(var(--crt-scan-strength) * var(--crt-amount)); }

@media (prefers-reduced-motion: reduce) {
  .crt-l-scan, .crt-l-roll, .crt-l-grain { animation: none; }
  .crt-l-roll { display: none; }
}
