@import '_content/Snopixis.UI/Snopixis.UI.9unna9465r.bundle.scp.css';

/* /Components/Decor/BubbleDecor.razor.rz.scp.css */
/* CSS scopé du décor « bulles de BD ». */
.decor--bubble[b-fctxg2en0y] { --decor-opacity: 0.38; }

.bubble-halftone__dot[b-fctxg2en0y] { fill: var(--color-primary); }

.bubble__shape[b-fctxg2en0y] {
    fill: color-mix(in srgb, var(--color-primary) 8%, transparent);
    stroke: var(--color-primary);
    stroke-width: 3.5;
    stroke-linejoin: round;
}

.bubble__lines path[b-fctxg2en0y] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 3.5;
    stroke-linecap: round;
    opacity: 0.55;
}

/* Les bulles « respirent » très lentement */
.bubble[b-fctxg2en0y] {
    transform-box: fill-box;
    transform-origin: center;
    animation: pf-bubble-breathe-b-fctxg2en0y 7s ease-in-out infinite;
}
.bubble--reply[b-fctxg2en0y] { animation-delay: 1.4s; }

@keyframes pf-bubble-breathe-b-fctxg2en0y {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.03); }
}

/* Les trois points de saisie, en cascade : « quelqu'un est en train d'écrire » */
.bubble__dot[b-fctxg2en0y] {
    fill: var(--color-primary);
    animation: pf-bubble-typing-b-fctxg2en0y 1.6s ease-in-out infinite;
}
.bubble__dot:nth-of-type(2)[b-fctxg2en0y] { animation-delay: 0.2s; }
.bubble__dot:nth-of-type(3)[b-fctxg2en0y] { animation-delay: 0.4s; }

@keyframes pf-bubble-typing-b-fctxg2en0y {
    0%, 60%, 100% { opacity: 0.3; translate: 0 0; }
    30%           { opacity: 1;   translate: 0 -7px; }
}

/* Les points tirent leur opacité de l'animation : on les rend lisibles sans elle. */
@media (prefers-reduced-motion: reduce) {
    .bubble__dot[b-fctxg2en0y] { opacity: 1; }
}
/* /Components/Decor/CircuitDecor.razor.rz.scp.css */
/* CSS scopé du décor « carte électronique ». La classe de base `.decor` (position, masque,
   révélation) reste globale : elle est partagée par les 5 décors. */
.decor--circuit[b-654karkqfs] { --decor-opacity: 0.4; }

.circuit__grid path[b-654karkqfs] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 1;
    opacity: 0.22;
}

.circuit__traces path[b-654karkqfs] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1600;
    stroke-dashoffset: 1600;
    animation: pf-circuit-draw-b-654karkqfs 3.2s ease forwards;
}
.circuit__traces path:nth-child(2)[b-654karkqfs]  { animation-delay: 0.15s; }
.circuit__traces path:nth-child(3)[b-654karkqfs]  { animation-delay: 0.3s; }
.circuit__traces path:nth-child(4)[b-654karkqfs]  { animation-delay: 0.45s; }
.circuit__traces path:nth-child(5)[b-654karkqfs]  { animation-delay: 0.6s; }
.circuit__traces path:nth-child(6)[b-654karkqfs]  { animation-delay: 0.75s; }
.circuit__traces path:nth-child(7)[b-654karkqfs]  { animation-delay: 0.9s; }
.circuit__traces path:nth-child(8)[b-654karkqfs]  { animation-delay: 1.05s; }
.circuit__traces path:nth-child(9)[b-654karkqfs]  { animation-delay: 1.2s; }
.circuit__traces path:nth-child(10)[b-654karkqfs] { animation-delay: 1.35s; }
.circuit__traces path:nth-child(11)[b-654karkqfs] { animation-delay: 1.5s; }
.circuit__traces path:nth-child(12)[b-654karkqfs] { animation-delay: 1.65s; }

/* Bon à savoir : dans un CSS scopé, Blazor renomme aussi les @keyframes (`pf-circuit-draw` →
   `pf-circuit-draw-b-xxx`) et réécrit la référence `animation:` en conséquence. Deux composants
   peuvent donc porter le même nom d'animation sans se marcher dessus — contrairement à app.css,
   qui est global et où un nom trop générique (`sx-spin`…) est un vrai risque. */
@keyframes pf-circuit-draw-b-654karkqfs {
    to { stroke-dashoffset: 0; }
}

.circuit__parts rect[b-654karkqfs] {
    fill: var(--color-primary);
    opacity: 0.75;
}

/* ---- La puce ---- */
.chip__body[b-654karkqfs] {
    fill: color-mix(in srgb, var(--color-primary) 12%, transparent);
    stroke: var(--color-primary);
    stroke-width: 3;
}
.chip__core[b-654karkqfs] {
    fill: color-mix(in srgb, var(--color-primary) 22%, transparent);
    stroke: var(--color-primary);
    stroke-width: 1.5;
}
.chip__dot[b-654karkqfs] { fill: var(--color-primary); }
.chip__pins rect[b-654karkqfs] { fill: var(--color-primary); opacity: 0.85; }

.circuit__chip[b-654karkqfs] {
    animation: pf-circuit-chip-b-654karkqfs 5s ease-in-out infinite;
}
@keyframes pf-circuit-chip-b-654karkqfs {
    0%, 100% { opacity: 0.8; }
    50%      { opacity: 1; }
}

/* ---- Vias : respiration de fond, indépendante de l'impulsion ---- */
.circuit__pads circle[b-654karkqfs] {
    fill: var(--color-primary);
    animation: pf-circuit-pad-b-654karkqfs 4s ease-in-out infinite;
}
.circuit__pads circle:nth-child(even)[b-654karkqfs] { animation-delay: 1.3s; }
.circuit__pads circle:nth-child(3n)[b-654karkqfs]   { animation-delay: 2.4s; }
@keyframes pf-circuit-pad-b-654karkqfs {
    0%, 100% { opacity: 0.35; }
    50%      { opacity: 1; }
}

/* ---- Easter egg : une impulsion part de la puce à chaque clic ----
   ⚠️ Le socle `.decor` est en `pointer-events: none` (app.css) précisément pour ne pas avaler
   clics et sélection de texte : on ne rouvre le pointeur que sur la puce elle-même. Rouvrir une
   forme précise ne gêne pas le défilement tactile — seul `touch-action` le ferait.
   ⚠️ En portrait, la puce (x=880 dans le dessin) est de toute façon HORS CHAMP : le rognage n'y
   laisse voir que la bande x ∈ [415, 785]. L'egg y est donc inopérant, faute de cible. */
.circuit__chip[b-654karkqfs] {
    pointer-events: auto;
    cursor: pointer;
}

/* Le calque d'impulsion ne doit jamais intercepter un nouveau clic sur la puce. */
.circuit__surge[b-654karkqfs] {
    pointer-events: none;
}

/* La comète : un court segment lumineux qui remonte la piste.
   `stroke-dasharray: 9 100` sur une piste normalisée à `pathLength="100"` → un trait de 9 suivi
   d'un vide plus long que la piste entière, donc un seul point lumineux à la fois. La durée et
   le retard viennent du C# (style inline), calculés depuis la longueur réelle. */
.surge__trace[b-654karkqfs] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 5;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 9 100;
    animation-name: pf-circuit-surge-b-654karkqfs;
    animation-timing-function: linear;   /* vitesse constante : un signal n'accélère pas */
    animation-fill-mode: both;
    filter: drop-shadow(0 0 6px var(--color-primary));
}

/* L'atténuation est gratuite : `pathLength` étant normalisé, l'avancement de l'animation EST la
   distance parcourue — une opacité qui décroît dans le temps décroît donc avec la distance. */
@keyframes pf-circuit-surge-b-654karkqfs {
    from { stroke-dashoffset: 9;    opacity: 1; }
    to   { stroke-dashoffset: -100; opacity: 0.08; }
}

/* Le flash d'un via, déclenché à l'instant exact où la comète l'atteint (retard calculé en C#). */
.surge__via[b-654karkqfs] {
    fill: var(--color-primary);
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
    animation-name: pf-circuit-via-b-654karkqfs;
    animation-duration: 0.5s;
    animation-fill-mode: both;
    filter: drop-shadow(0 0 8px var(--color-primary));
}
@keyframes pf-circuit-via-b-654karkqfs {
    0%   { opacity: 0; transform: scale(1); }
    25%  { opacity: 1; transform: scale(2.2); }
    100% { opacity: 0; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    /* Les pistes se révèlent par animation : sans elle, elles resteraient invisibles. */
    .circuit__traces path[b-654karkqfs] { stroke-dashoffset: 0; }
    .circuit__pads circle[b-654karkqfs] { opacity: 1; }
    /* ⚠️ Animations coupées, le calque d'impulsion figerait sa comète au départ de chaque piste
       — un moignon lumineux permanent. On le masque : l'egg est purement décoratif. */
    .circuit__surge[b-654karkqfs] { display: none; }
}
/* /Components/Decor/ComicDecor.razor.rz.scp.css */
/* CSS scopé du décor « trame de bande dessinée ». */
.decor--comic[b-b7a3d0jgte] { --decor-opacity: 0.36; }

/* ⚠️ Les deux nappes partagent le MÊME <pattern> : leurs points ne peuvent pas être de couleurs
   différentes (le contenu du pattern n'est pas un descendant des <rect>). On les différencie
   donc par l'opacité de la nappe, pas par la couleur des points. */
.halftone__dot[b-b7a3d0jgte] { fill: var(--color-primary); }
.halftone--alt[b-b7a3d0jgte] { opacity: 0.55; }

.comic__speed path[b-b7a3d0jgte] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 3;
    stroke-linecap: round;
    opacity: 0.6;
}

/* ⚠️ L'animation vit sur la forme INTERNE, jamais sur l'element qui porte l'attribut
   `transform` : une propriete CSS `transform`/`scale` s'applique AVANT lui et le remplace.
   L'enveloppe `.comic__burst` ne fait que positionner. */
.comic__burst-shape[b-b7a3d0jgte] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 2.5;
    stroke-linejoin: round;
    opacity: 0.5;
    transform-box: fill-box;
    transform-origin: center;
    animation: pf-comic-burst-b-b7a3d0jgte 6s ease-in-out infinite;
}
@keyframes pf-comic-burst-b-b7a3d0jgte {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.06); }
}

.comic__panel path[b-b7a3d0jgte] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 4;
    stroke-linecap: round;
    opacity: 0.5;
}

/* ---- Easter egg : l'onomatopee ----
   Le socle `.decor` est en `pointer-events: none` (app.css) pour ne pas avaler clics et selection
   de texte : on ne rouvre le pointeur que sur l'eclat.
   ⚠️ C'est le SEUL des trois eggs qui marche aussi en portrait, parce qu'il est a la fois centre
   horizontalement (donc epargne par le rognage, qui ne laisse voir que x ∈ [415, 785]) et dans la
   bande haute libre de contenu. Les deux conditions a reunir pour tout format. */
.comic__burst[b-b7a3d0jgte] {
    pointer-events: auto;
    cursor: pointer;
}
/* ⚠️ `all` et non `auto` : l'eclat est un CONTOUR (`fill: none`), et en SVG une forme sans
   remplissage n'est cliquable que sur son trait — 2,5 px de large, injouable. `all` la rend
   sensible sur toute sa surface interieure, sans pour autant la remplir visuellement. */
.comic__burst-shape[b-b7a3d0jgte] {
    pointer-events: all;
}

/* Le calque de l'onomatopee ne doit jamais intercepter un nouveau clic sur l'eclat. */
.comic__pow[b-b7a3d0jgte] {
    pointer-events: none;
}

/* ⚠️ Lettrage a la police du site, pas une vraie fonte BD : on compense par du tres gras, une
   inclinaison et un contour epais, qui donnent l'essentiel du caractere « case d'action ».
   `paint-order: stroke` fait passer le contour DERRIERE le remplissage, sinon il rongerait
   l'interieur des lettres et les rendrait illisibles a cette graisse. */
.pow__word[b-b7a3d0jgte] {
    font-size: 96px;
    font-weight: 900;
    font-style: italic;
    letter-spacing: 2px;
    fill: var(--color-primary);
    stroke: var(--color-bg);
    stroke-width: 8;
    paint-order: stroke;
    transform-box: fill-box;
    transform-origin: center;
    animation: pf-comic-pow-b-b7a3d0jgte 1.1s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}

/* Jaillissement : arrivee brutale en sur-echelle, leger contrecoup, puis effacement. */
@keyframes pf-comic-pow-b-b7a3d0jgte {
    0%   { opacity: 0; scale: 0.2; rotate: -18deg; }
    30%  { opacity: 1; scale: 1.25; rotate: 4deg; }
    45%  { opacity: 1; scale: 1; rotate: -2deg; }
    70%  { opacity: 1; scale: 1.02; rotate: 0deg; }
    100% { opacity: 0; scale: 1.1; rotate: 0deg; }
}

@media (prefers-reduced-motion: reduce) {
    /* ⚠️ Animations coupees, le mot resterait affiche en permanence par-dessus le decor.
       On masque le calque : l'egg est purement decoratif. */
    .comic__pow[b-b7a3d0jgte] { display: none; }
}
/* /Components/Decor/CubesDecor.razor.rz.scp.css */
/* CSS scopé du décor « Rubik's ». */
.decor--cubes[b-a3ql5sys0y] { --decor-opacity: 0.42; }

/* Le contour couleur du fond fait office d'interstice entre facettes (marche dans les 2 thèmes) */
.decor--cubes .facet[b-a3ql5sys0y] {
    stroke: var(--color-bg);
    stroke-width: 2;
    stroke-linejoin: round;
}

/* ---- Easter egg : le cube se résout au clic ----
   La bascule C# ne fait que changer les couleurs ; c'est cette transition qui produit le fondu,
   et le décalage par face qui donne la cascade « une face après l'autre ».
   Sous `prefers-reduced-motion`, la coupure globale des transitions (app.css) rend la résolution
   instantanée — l'egg reste jouable, il perd juste son animation. */
.facet[b-a3ql5sys0y] {
    transition: fill 0.45s ease;
}
.facet--left[b-a3ql5sys0y]  { transition-delay: 0.12s; }
.facet--right[b-a3ql5sys0y] { transition-delay: 0.24s; }

/* ⚠️ Souris uniquement. Sur tactile le décor doit rester traversant : il couvre TOUTE la section,
   un tap intercepté gênerait la lecture — et l'egg serait de toute façon invisible, le masque
   radial n'exposant le décor que sur les bords, recouverts par le contenu en petite largeur. */
@media (hover: hover) and (pointer: fine) {
    /* Le socle `.decor` est en `pointer-events: none` (app.css) précisément pour ne pas avaler
       clics et sélection de texte : on ne rouvre le pointeur que sur les formes concernées.
       ⚠️ Le déclencheur est le cubie « clé », pas les gros cubes : mesuré, ceux-ci sont
       recouverts à 100 % par les cartes de compétences (le décor est sous le contenu). On les
       laisse tout de même cliquables — gratuit, et utile sur un écran très large. */
    .cubie--key[b-a3ql5sys0y],
    .cube-main[b-a3ql5sys0y] {
        pointer-events: auto;
        cursor: pointer;
    }
    /* Indice de découvrabilité, volontairement discret.
       ⚠️ Surtout pas `transform` : il écraserait l'attribut `transform` qui place la forme dans
       la scène (une propriété de présentation SVG se fait battre par sa contrepartie CSS). */
    .cubie--key:hover[b-a3ql5sys0y],
    .cube-main:hover[b-a3ql5sys0y] {
        filter: brightness(1.3);
    }
}

.cube-main[b-a3ql5sys0y],
.cubie[b-a3ql5sys0y] {
    animation: pf-cubes-float-b-a3ql5sys0y 16s ease-in-out infinite;
}
.cube-main--alt[b-a3ql5sys0y] { animation-duration: 20s; animation-delay: 3s; }
.cubie:nth-of-type(even)[b-a3ql5sys0y] { animation-delay: 2s; animation-duration: 12s; }

@keyframes pf-cubes-float-b-a3ql5sys0y {
    0%, 100% { translate: 0 0; }
    50%      { translate: 0 -18px; }
}
/* /Components/Decor/MapDecor.razor.rz.scp.css */
/* CSS scopé du décor « carte marine ». */
.decor--map[b-fzy1u6ppoy] {
    --decor-opacity: 0.38;
    --map-ink: var(--color-warning, #d97706);
}

.map__grid path[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 1;
    opacity: 0.28;
}

.map__waves path[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 2;
    opacity: 0.5;
}

.map__land[b-fzy1u6ppoy] {
    fill: var(--map-ink);
    fill-opacity: 0.22;
    stroke: var(--map-ink);
    stroke-width: 2.5;
    stroke-linejoin: round;
}

.map__islands path[b-fzy1u6ppoy] {
    fill: var(--map-ink);
    fill-opacity: 0.26;
    stroke: var(--map-ink);
    stroke-width: 2;
}

.map__ring[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 2;
}
.map__ring--thin[b-fzy1u6ppoy] { stroke-width: 1.2; }

.map__star[b-fzy1u6ppoy] {
    fill: var(--map-ink);
    fill-opacity: 0.2;
    stroke: var(--map-ink);
    stroke-width: 2;
    stroke-linejoin: round;
}
.map__star--thin[b-fzy1u6ppoy] { fill-opacity: 0; stroke-width: 1.2; }

.map__ticks[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 2.5;
}

/* ⚠️ L'animation vit sur le groupe INTERNE, jamais sur celui qui porte l'attribut `transform`.
   Une propriété CSS `transform`/`rotate`/`scale` s'applique AVANT l'attribut et le remplace :
   mesuré, la rose dérivait de (1010,190) vers (84,16) en 120 s — et ne tournait même pas, la
   matrice restant sans composante de rotation. Le cadran, lui, n'a pas d'attribut : il tourne
   proprement autour de son centre. */
.map__compass-dial[b-fzy1u6ppoy] {
    transform-box: fill-box;
    transform-origin: center;
    animation: pf-map-spin-b-fzy1u6ppoy 120s linear infinite;
}
@keyframes pf-map-spin-b-fzy1u6ppoy {
    to { transform: rotate(360deg); }
}

.map__route[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 3.5;
    stroke-linecap: round;
    stroke-dasharray: 14 18;
    animation: pf-map-sail-b-fzy1u6ppoy 40s linear infinite;   /* la route « avance » */
}
@keyframes pf-map-sail-b-fzy1u6ppoy {
    to { stroke-dashoffset: -640; }
}

.map__waypoints circle[b-fzy1u6ppoy] {
    fill: var(--color-bg);
    stroke: var(--map-ink);
    stroke-width: 3.5;
}

.map__x path[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 5;
    stroke-linecap: round;
}

/* ---- Easter egg : la traversee ----
   Un clic sur la rose des vents lance le voyage. Les durees et retards viennent du C# (style
   inline), calcules depuis la longueur reelle de la route.
   Souris uniquement : sur tactile le decor doit rester traversant, il couvre toute la section. */
/* Le socle `.decor` est en `pointer-events: none` (app.css) pour ne pas avaler clics et
   selection de texte : on ne rouvre le pointeur que sur la rose.
   ⚠️ En portrait, seule sa partie gauche reste visible (le rognage ne laisse que x ∈ [464, 736]
   du dessin) : l'egg y est jouable mais peu confortable. */
.map__compass[b-fzy1u6ppoy] {
    pointer-events: auto;
    cursor: pointer;
}
/* ⚠️ Meme piege que l'eclat BD : les anneaux sont en `fill: none`, donc cliquables seulement sur
   leur trait. `all` rend leur disque entier sensible — l'etoile centrale, elle, est deja remplie
   et repondait donc deja. */
.map__ring[b-fzy1u6ppoy] {
    pointer-events: all;
}

/* Le calque du voyage ne doit jamais intercepter un nouveau clic sur la rose. */
.map__surge[b-fzy1u6ppoy] {
    pointer-events: none;
}

/* Onde de depart, facon ping de sonar */
.surge__ping[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 3;
    transform-box: fill-box;
    transform-origin: center;
    animation: pf-map-ping-b-fzy1u6ppoy 0.9s ease-out both;
}
@keyframes pf-map-ping-b-fzy1u6ppoy {
    from { opacity: 0.9; scale: 0.55; }
    to   { opacity: 0;   scale: 1.9; }
}

/* La trace qui remonte la route. `stroke-dasharray: 8 100` sur un tracé normalisé à
   `pathLength="100"` → un seul point lumineux a la fois, qui parcourt toute la route. */
.surge__route[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 6;
    stroke-linecap: round;
    stroke-dasharray: 8 100;
    animation-name: pf-map-voyage-b-fzy1u6ppoy;
    animation-timing-function: linear;   /* vitesse constante : un navire ne teleporte pas */
    animation-fill-mode: both;
    filter: drop-shadow(0 0 7px var(--map-ink));
}
/* Attenuation volontairement douce, contrairement au circuit : ici on veut voir la trace
   ARRIVER au tresor, pas s'eteindre en chemin. */
@keyframes pf-map-voyage-b-fzy1u6ppoy {
    from { stroke-dashoffset: 8;    opacity: 1; }
    to   { stroke-dashoffset: -100; opacity: 0.55; }
}

/* Chaque etape s'allume a l'instant precis ou la trace la traverse */
.surge__stop[b-fzy1u6ppoy] {
    fill: var(--map-ink);
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
    animation: pf-map-stop-b-fzy1u6ppoy 0.6s ease-out both;
    filter: drop-shadow(0 0 8px var(--map-ink));
}
@keyframes pf-map-stop-b-fzy1u6ppoy {
    0%   { opacity: 0; scale: 1; }
    30%  { opacity: 1; scale: 2.4; }
    100% { opacity: 0; scale: 1; }
}

/* L'arrivee : la croix s'illumine et se pose */
/* Même séparation que la rose : l'enveloppe `.surge__treasure` ne fait que positionner (son
   attribut `transform`), c'est la marque interne qui grossit et pivote. Les mélanger faisait
   voyager la croix à travers l'écran au lieu de la poser sur le trésor. */
.surge__treasure-mark[b-fzy1u6ppoy] {
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
    animation: pf-map-treasure-b-fzy1u6ppoy 1.4s ease-out both;
    filter: drop-shadow(0 0 12px var(--map-ink));
}
.surge__treasure path[b-fzy1u6ppoy] {
    fill: none;
    stroke: var(--map-ink);
    stroke-width: 7;
    stroke-linecap: round;
}
@keyframes pf-map-treasure-b-fzy1u6ppoy {
    0%   { opacity: 0; scale: 0.4; rotate: -30deg; }
    22%  { opacity: 1; scale: 1.6; rotate: 0deg; }
    45%  { opacity: 1; scale: 1.15; }
    100% { opacity: 0; scale: 1.15; }
}

@media (prefers-reduced-motion: reduce) {
    /* ⚠️ Animations coupees, la trace figerait son point lumineux au depart de la route et le
       ping resterait affiche. On masque le calque : l'egg est purement decoratif. */
    .map__surge[b-fzy1u6ppoy] { display: none; }
}
/* /Components/Sections/ContactCard.razor.rz.scp.css */
/* Carte de visite du contact.

   ⚠️ La racine de ce composant est un `<SxCard>`, donc un COMPOSANT : elle ne porte pas
   l'attribut de scope, et `::deep` n'a ici aucun ancêtre où s'accrocher. Ne peuvent donc vivre
   dans ce fichier que les règles visant des éléments HTML écrits directement ci-contre
   (`<div class="vcard__monogram">`, `<svg class="vcard__icon">`) — eux le portent bien.
   Les deux règles qui doivent traverser un composant enfant (l'alignement des SxLinkButton et
   le repli mobile) vivent dans ContactSection.razor.css, qui dispose d'une `<section>` scopée
   comme ancêtre. */

.vcard__monogram[b-v6a1yhqddi] {
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 1.1rem;
    color: var(--color-text-inverse, #fff);
    background-color: var(--color-primary);
}

.vcard__icon[b-v6a1yhqddi] {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}
/* Les logos de marque sont des aplats, pas des tracés */
.vcard__icon--solid[b-v6a1yhqddi] {
    fill: currentColor;
    stroke: none;
}
/* /Components/Sections/ContactSection.razor.rz.scp.css */
/* CSS de la section Contact. La carte de visite a son propre fichier (ContactCard.razor.css) :
   ici on ne traite que la section et ce qu'elle rend elle-même. */

/* Fond légèrement contrasté : une section sur deux, pour marquer le rythme.
   Pas de `::deep` — c'est la racine, elle porte l'attribut de scope. */
.section-band[b-6skcgdhye8] {
    background-color: color-mix(in srgb, var(--color-text-primary) 4%, transparent);
}

/* ⚠️ `align-items: start` : sans ça la grille étire la carte de visite à la hauteur du
   formulaire (mesuré : 462px de carte pour 125px de contenu, soit 73% de vide). */
[b-6skcgdhye8] .sx-grid {
    align-items: start;
}

/* ---- Carte de visite : les deux règles qui doivent traverser un composant ----
   Le reste du style de la carte vit dans ContactCard.razor.css. Ces deux-là ne peuvent pas :
   elles ciblent ce que rendent SxLinkButton et SxStack, or la racine de ContactCard est un
   `<SxCard>` qui ne porte aucun scope. La `<section>` d'ici, si — d'où leur présence.
   ⚠️ SxLinkButton centre son contenu par défaut (seul SxNavMenu corrige ça, depuis son propre
   CSS scopé) → hors nav il faut le recaler soi-même. */
[b-6skcgdhye8] .vcard__links .sx-link {
    justify-content: flex-start;
    gap: var(--space-2);
}

/* ---- Avion en papier qui s'envole à l'envoi ----
   Écrit directement dans ce .razor : porte le scope, pas de `::deep`. */
.paper-plane[b-6skcgdhye8] {
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 1.6;
    stroke-linejoin: round;
    opacity: 0;
}
.paper-plane[data-flying][b-6skcgdhye8] {
    animation: pf-contact-fly-b-6skcgdhye8 1.1s ease-out;
}
@keyframes pf-contact-fly-b-6skcgdhye8 {
    0%   { opacity: 1; transform: translate(0, 0) rotate(0deg) scale(1); }
    70%  { opacity: 1; }
    100% { opacity: 0; transform: translate(90px, -46px) rotate(18deg) scale(0.75); }
}

/* ---- La carte s'anime quand elle entre dans la vue ----
   `sx-rise` est déclaré dans app.css (partagé — cf. ProjectsSection.razor.css). */
[b-6skcgdhye8] .sx-card {
    opacity: 0;
}
[b-6skcgdhye8] .sx-card[data-in-view] {
    animation: sx-rise 0.55s ease both;
}
[b-6skcgdhye8] .sx-card[data-in-view]:nth-child(2) { animation-delay: 0.08s; }

@media (max-width: 640px) {
    /* Les deux cartes empilées se resserrent : empilée sous le formulaire, la section passait
       à 944px pour 812 d'écran → bas difficile à atteindre avec le snap `mandatory`. */
    [b-6skcgdhye8] .sx-grid {
        gap: var(--space-2);
    }
    /* Le champ Message n'a pas besoin d'être aussi haut sur mobile */
    [b-6skcgdhye8] textarea {
        min-height: 64px;
    }
    /* Carte de visite compacte : on retire les infos pratiques (secondaires) et on met les
       liens sur une rangée. ⚠️ SxStack impose `flex-direction: column` depuis son CSS scopé
       (0-2-0) ; `[b-xxx] .vcard__links` vaut autant, mais le CSS scopé de l'app est concaténé
       APRÈS celui du RCL → il gagne l'égalité. C'est ce qui remplace l'ancien préfixe `#contact`. */
    [b-6skcgdhye8] .vcard__practical {
        display: none;
    }
    [b-6skcgdhye8] .vcard__links {
        flex-direction: row;
        flex-wrap: wrap;
    }
    [b-6skcgdhye8] .vcard__links .sx-link {
        width: auto;
    }
}

/* La coupure globale des animations vit dans app.css ; ici on redonne l'état final,
   sinon la carte resterait invisible. L'avion reste masqué : purement décoratif. */
@media (prefers-reduced-motion: reduce) {
    [b-6skcgdhye8] .sx-card { opacity: 1; }
}
/* /Components/Sections/ProjectsSection.razor.rz.scp.css */
/* CSS de la section Projets.

   Pourquoi `::deep` partout : l'attribut de scope n'est posé que sur les éléments HTML écrits
   dans ce .razor — donc sur `<section>`, mais PAS sur ce que rendent SxCard/SxImage/SxCarousel.
   `::deep X` déplace l'attribut sur l'ancêtre (`section[b-xxx] X`), ce qui atteint ces enfants.

   ⚠️ C'est aussi ce qui remplace l'ancien préfixe `#projets`. Il n'était pas cosmétique : dans
   app.css (chargé AVANT le bundle du RCL), `.sx-card` (0-1-0) perdait contre la règle scopée
   `.sx-card[b-yyy]` du RCL (0-2-0). Ici `[b-xxx] .sx-card` vaut 0-2-0 ET le CSS scopé de l'app
   est concaténé APRÈS celui du RCL → on gagne les égalités. L'id ne sert donc plus à rien. */

/* Largeur fixe de carte : c'est elle qui rend la hauteur de la section constante,
   donc le snap page-par-page fiable, quel que soit le nombre de projets. */
[b-lkb883tqvd] .project-card {
    flex: 0 0 var(--pf-card-width);
    max-width: var(--pf-card-width);
}

/* Résumé borné à 3 lignes : une description longue ne doit pas faire grandir la carte */
[b-lkb883tqvd] .project-card__summary {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}

/* ---- Pied de carte collé en bas ----
   La piste du carrousel étire déjà toutes les cartes à la même hauteur, mais SxCard rend un
   `.sx-card__body` en `display: block` : la colonne interne s'arrêtait à la hauteur de son
   contenu (mesuré : 346px dans un corps de 378px) et `margin-top: auto` n'avait aucun espace
   libre à absorber. On rétablit donc la chaîne d'étirement jusqu'au pied. */
[b-lkb883tqvd] .project-card > .sx-card__body {
    display: flex;
    flex-direction: column;
}
[b-lkb883tqvd] .project-card > .sx-card__body > .sx-stack {
    flex: 1;
}
[b-lkb883tqvd] .project-card__footer {
    margin-top: auto;   /* absorbe l'écart de longueur des résumés ET le retour à la ligne des badges */
}

/* ---- Élévation + zoom de l'image au survol ---- */
[b-lkb883tqvd] .sx-card {
    transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
[b-lkb883tqvd] .sx-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 14px 30px rgba(0, 0, 0, 0.28);
    border-color: var(--color-primary);
}
[b-lkb883tqvd] .sx-card .sx-image {
    overflow: hidden;
}
[b-lkb883tqvd] .sx-card .sx-image img {
    transition: transform 0.4s ease;
}
[b-lkb883tqvd] .sx-card:hover .sx-image img {
    transform: scale(1.05);
}

/* ---- Chaque carte s'anime quand ELLE entre dans la vue ----
   (`data-in-view` est posé par élément, pas par section — cf. reveal.js)
   `sx-rise` reste déclaré dans app.css : dans un fichier scopé, Blazor ne renomme QUE les
   @keyframes déclarés dans ce même fichier (vérifié sur le bundle généré), un nom externe est
   laissé intact. Une animation réellement partagée — ici le hero et 3 sections — se déclare
   donc une fois en global plutôt qu'être recopiée dans chaque composant. */
[b-lkb883tqvd] .sx-card {
    opacity: 0;
}
[b-lkb883tqvd] .sx-card[data-in-view] {
    animation: sx-rise 0.55s ease both;
}
/* Léger décalage quand plusieurs cartes entrent ensemble */
[b-lkb883tqvd] .sx-card[data-in-view]:nth-child(2)    { animation-delay: 0.08s; }
[b-lkb883tqvd] .sx-card[data-in-view]:nth-child(3)    { animation-delay: 0.16s; }
[b-lkb883tqvd] .sx-card[data-in-view]:nth-child(n+4)  { animation-delay: 0.24s; }

@media (max-width: 640px) {
    /* Les flèches étant masquées par le carrousel lui-même, la piste récupère toute la largeur.
       86% : la carte suivante dépasse légèrement — c'est ce qui indique qu'on peut défiler. */
    [b-lkb883tqvd] .project-card {
        flex: 0 0 86%;
        max-width: 86%;
    }
}

/* La coupure globale des animations vit dans app.css ; ici on REDONNE l'état final,
   sinon les cartes resteraient invisibles pour qui préfère les animations réduites. */
@media (prefers-reduced-motion: reduce) {
    [b-lkb883tqvd] .sx-card { opacity: 1; }
}
/* /Components/Sections/SectionHeading.razor.rz.scp.css */
/* Titre de section numéroté + filet qui se dessine au reveal.

   ⚠️ La racine est un `<SxStack>` (un composant) : elle ne porte pas l'attribut de scope, donc
   `.section-heading` elle-même n'est PAS ciblable ici. En revanche le `<svg>` et son `<path>`
   sont écrits directement dans ce .razor : ils portent le scope, et c'est sur le DERNIER élément
   du sélecteur que Blazor le pose — d'où `.section-heading[data-in-view] … path` qui fonctionne
   malgré un ancêtre non scopé. */

.section-heading__rule[b-zcuiky4dte] {
    display: block;
    width: 120px;
    height: 4px;
    overflow: visible;
}
.section-heading__rule path[b-zcuiky4dte] {
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-dasharray: 200;
    stroke-dashoffset: 200;
}
/* `data-in-view` est posé par reveal.js sur la racine `.section-heading` */
.section-heading[data-in-view] .section-heading__rule path[b-zcuiky4dte] {
    animation: pf-heading-draw-b-zcuiky4dte 0.8s ease 0.15s forwards;
}
@keyframes pf-heading-draw-b-zcuiky4dte {
    to { stroke-dashoffset: 0; }
}

/* Le filet tire son tracé de l'animation : sans elle, il resterait invisible. */
@media (prefers-reduced-motion: reduce) {
    .section-heading__rule path[b-zcuiky4dte] { stroke-dashoffset: 0; }
}
/* /Components/Sections/SkillsSection.razor.rz.scp.css */
/* CSS de la section Compétences.
   L'anneau de niveau lui-même est un composant du RCL (SxProgressRing) : il porte son propre
   CSS scopé. Ne reste ici que la mise en page propre à la section.

   ⚠️ Le bloc `#competences .sx-badge` (chips au survol) qui vivait dans app.css a été retiré :
   il ne correspondait à rien — la section n'affiche aucun SxBadge (vérifié dans le DOM rendu),
   les badges sont ceux des technologies, dans la section Projets. */

/* Colonne d'une compétence : `.skill` est posée sur un SxStack, donc `::deep`. */
[b-fs00ove5sr] .skill {
    width: 76px;
}
/* /Components/Sections/TimelineSection.razor.rz.scp.css */
/* CSS de la section Parcours. `::deep` dès qu'on cible ce que rend un composant enfant
   (SxCard, SxGrid) : eux ne portent pas l'attribut de scope, seule la `<section>` racine
   le porte. Cf. l'en-tête de ProjectsSection.razor.css pour le détail. */

/* Fond légèrement contrasté : une section sur deux, pour marquer le rythme.
   Pas de `::deep` ici — c'est la racine elle-même, qui porte l'attribut. */
.section-band[b-xxbh9374wb] {
    background-color: color-mix(in srgb, var(--color-text-primary) 4%, transparent);
}

/* Icône dessinée en tête d'entrée (mallette ou toque). Écrite directement dans ce .razor :
   elle porte donc le scope, pas besoin de `::deep`. */
.timeline__icon[b-xxbh9374wb] {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    fill: none;
    stroke: var(--color-primary);
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* ---- Point d'accent en marge de chaque entrée ----
   ⚠️ Pas de trait vertical reliant les entrées : il supposait UNE colonne, or la section est
   en grille responsive (2 colonnes en desktop) pour borner sa hauteur. Le point suffit —
   l'idée de trajet est portée par la route de la carte marine, en fond de section. */
[b-xxbh9374wb] .sx-card {
    position: relative;
    margin-left: var(--space-6);
}
[b-xxbh9374wb] .sx-card::before {
    content: "";
    position: absolute;
    left: calc(-1 * var(--space-6));
    top: 1.25rem;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--color-primary);
    box-shadow: 0 0 0 4px var(--color-bg);
    z-index: 1;
}
/* Entrée en cours (EndDate null) : le point pulse */
[b-xxbh9374wb] .sx-card[data-current]::before {
    animation: pf-timeline-pulse-b-xxbh9374wb 2s ease-in-out infinite;
}
@keyframes pf-timeline-pulse-b-xxbh9374wb {
    0%, 100% { box-shadow: 0 0 0 4px var(--color-bg), 0 0 0 4px color-mix(in srgb, var(--color-primary) 45%, transparent); }
    50%      { box-shadow: 0 0 0 4px var(--color-bg), 0 0 0 11px color-mix(in srgb, var(--color-primary) 0%, transparent); }
}

/* ---- Chaque carte s'anime quand ELLE entre dans la vue ----
   `sx-rise` est déclaré dans app.css : partagé par le hero et 3 sections, il reste global
   (un @keyframes externe n'est pas renommé par le scoping — cf. ProjectsSection.razor.css). */
[b-xxbh9374wb] .sx-card {
    opacity: 0;
}
[b-xxbh9374wb] .sx-card[data-in-view] {
    animation: sx-rise 0.55s ease both;
}
[b-xxbh9374wb] .sx-card[data-in-view]:nth-child(2)   { animation-delay: 0.08s; }
[b-xxbh9374wb] .sx-card[data-in-view]:nth-child(3)   { animation-delay: 0.16s; }
[b-xxbh9374wb] .sx-card[data-in-view]:nth-child(n+4) { animation-delay: 0.24s; }

/* La coupure globale des animations vit dans app.css ; ici on redonne l'état final,
   sinon les cartes resteraient invisibles. */
@media (prefers-reduced-motion: reduce) {
    [b-xxbh9374wb] .sx-card { opacity: 1; }
}
/* /Layout/MainLayout.razor.rz.scp.css */
.portfolio-root[b-57rfh191dz] {
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

.portfolio-topbar[b-57rfh191dz] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-4) var(--space-6);
    border-bottom: 1px solid var(--color-border);
}

.portfolio-content[b-57rfh191dz] {
    flex: 1;
}

#blazor-error-ui[b-57rfh191dz] {
    background: var(--color-danger);
    color: var(--color-text-inverse);
    bottom: 0;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.3);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.75rem 3rem 0.75rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 99999;
}

#blazor-error-ui a[b-57rfh191dz] {
    color: var(--color-text-inverse);
    font-weight: var(--font-weight-semibold);
    text-decoration: underline;
}

#blazor-error-ui .dismiss[b-57rfh191dz] {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 50%;
    transform: translateY(-50%);
}
/* /Pages/HomePage.razor.rz.scp.css */
/* CSS du hero. Le seul élément HTML brut de cette page est la `<section id="accueil">` :
   c'est elle qui porte l'attribut de scope, donc `::deep` ne peut atteindre QUE le contenu du
   hero — les 4 autres sections, rendues par leurs propres composants, sont hors de portée et
   ont chacune leur .razor.css. */

/* Le hero respire plus serré que les autres sections (l'équivalent de l'ancien GapSize.Sm).
   `.section-band[b-xxx]` (0-2-0) l'emporte sur le `.section-band` global (0-1-0). */
.section-band[b-yhalji4bj5] {
    gap: var(--space-2);
}

[b-yhalji4bj5] .name__title {
    font-size: calc(var(--text-xxl-size) * 2);
}

/* ---- Aurora animée en fond ---- */
.section-band[b-yhalji4bj5]::before {
    content: "";
    position: absolute;
    inset: -40%;
    z-index: 0;
    filter: blur(48px);
    background:
        radial-gradient(38% 38% at 28% 32%, color-mix(in srgb, var(--color-primary) 40%, transparent), transparent 70%),
        radial-gradient(34% 34% at 72% 62%, color-mix(in srgb, var(--pf-aurora-secondary) 34%, transparent), transparent 70%);
    animation: pf-hero-aurora-b-yhalji4bj5 16s ease-in-out infinite alternate;
}
@keyframes pf-hero-aurora-b-yhalji4bj5 {
    from { transform: translate3d(-4%, -2%, 0) rotate(0deg); }
    to   { transform: translate3d(4%, 3%, 0) rotate(8deg); }
}

/* ---- Laisser le clic atteindre la puce du décor ----
   La règle globale `.section-band > *` donne à chaque enfant direct toute la largeur de lecture
   (1080px). Pour deux lignes courtes, la moitié droite de la boîte est vide — et elle recouvrait
   la puce, avalant le clic de l'easter egg (mesuré : 33 % de la puce inatteignable à 1280).

   Deux corrections complémentaires, l'une ne suffit pas sans l'autre :
   - la PILE reste large de 1080px mais devient traversante — c'est un conteneur vide, il n'y a
     rien à y sélectionner, donc rien à perdre ;
   - les TEXTES, eux, se réduisent à leur contenu (`AlignItems=Start`) et récupèrent le pointeur :
     ils ne bloquent donc que là où il y a vraiment du texte, et restent sélectionnables. */
[b-yhalji4bj5] .hero__intro {
    pointer-events: none;
}
[b-yhalji4bj5] .hero__intro .sx-text {
    pointer-events: auto;
}

/* Entrée du titre et du sous-titre (rendus par SxText → `::deep`).
   `sx-rise` est déclaré dans app.css : partagé avec les cartes des sections, il reste global. */
[b-yhalji4bj5] .sx-text {
    animation: sx-rise 0.7s ease both;
}

/* ---- Invite à faire défiler ----
   Le `<a>` est écrit directement dans ce .razor : il porte le scope, pas de `::deep`. */
.scroll-cue[b-yhalji4bj5] {
    position: absolute;
    left: 50%;
    bottom: var(--space-4);
    z-index: 2;
    color: var(--color-text-secondary, var(--color-text-primary));
    animation: pf-hero-bounce-b-yhalji4bj5 2.2s ease-in-out infinite;
}
.scroll-cue svg[b-yhalji4bj5] {
    display: block;
    width: 30px;
    height: 30px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
/* Le translate(-50%) fait partie de l'animation : il porte aussi le centrage horizontal,
   d'où sa présence dans CHAQUE étape (sinon la flèche saute à gauche quand on coupe tout). */
@keyframes pf-hero-bounce-b-yhalji4bj5 {
    0%, 100% { transform: translate(-50%, 0); opacity: 0.55; }
    50%      { transform: translate(-50%, 8px); opacity: 1; }
}

/* ⚠️ Animations coupées : le centrage de la flèche venait du `transform` de l'animation.
   Sans lui elle repartirait à gauche → on le repose explicitement. */
@media (prefers-reduced-motion: reduce) {
    .scroll-cue[b-yhalji4bj5] {
        transform: translate(-50%, 0);
        opacity: 0.55;
    }
}
