/* ==========================================================================
   pied2biche — feuille unique, écrite du téléphone vers le bureau.

   Le visiteur arrive d'Instagram : téléphone tenu à une main, en vertical,
   souvent dans le navigateur intégré de l'application. Toutes les règles de
   base visent donc cette colonne étroite ; les `min-width` qui suivent ne font
   qu'élargir ce qui existe déjà, jamais l'inverse.
   ========================================================================== */

/* ---------------------------------------------------------- jetons */
:root {
    --fond: #FDF6F8;
    --surface: #FFFFFF;
    --bordure: #F0DCE3;
    --texte: #4A2545;
    --texte-doux: #8A7078;
    --accent-texte: #C2185B;
    --accent-fond: #C2185B;
    --accent-survol: #7A0F38;
    --or: #C9A227;

    /* Encre du seul élément posé SUR le doré (l'étiquette du pack intégral).
       Le blanc sur #C9A227 plafonne à 2,3:1 ; cette encre, reprise du fond
       sombre de la palette, monte à 8,7:1 sans introduire de teinte nouvelle. */
    --or-encre: #1B0E18;

    --serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
    --sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --rayon: 11px;
    --rayon-doux: 8px;

    /* Rythme. Une seule marge latérale pour toute la page : c'est elle qui
       garantit qu'aucun bloc ne peut dépasser la largeur de l'écran. */
    --marge-page: clamp(18px, 5vw, 30px);
    --espace-bloc: clamp(36px, 8vw, 60px);
    --gouttiere: clamp(9px, 2.4vw, 14px);

    /* Typographie fluide : aucune cassure entre 320 px et 1280 px. */
    --t-corps: clamp(1rem, 0.97rem + 0.14vw, 1.0625rem);
    --t-petit: clamp(0.8125rem, 0.79rem + 0.1vw, 0.875rem);
    --t-mini: 0.6875rem;
    --t-titre: clamp(1.15rem, 1.05rem + 0.5vw, 1.4rem);
    --t-nom: clamp(2rem, 1.35rem + 3.2vw, 2.9rem);
    --t-geant: clamp(2.1rem, 1.45rem + 3.2vw, 3rem);
    --t-nombre: clamp(2.2rem, 1.6rem + 3vw, 3.2rem);
    --t-prix: clamp(1.6rem, 1.35rem + 1.2vw, 2.05rem);

    --hauteur-barre: 68px;
}

/* `--accent-fond` n'est volontairement PAS redéfini ici : le contraste d'un
   bouton se joue entre son texte blanc et sa propre couleur de fond, pas avec
   la page. L'éclaircir le ferait tomber à 3,7:1, sous le seuil AA. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="clair"]) {
        --fond: #1B0E18;
        --surface: #2A1826;
        --bordure: #3D2438;
        --texte: #F7EAF0;
        --texte-doux: #B9A0AC;
        --accent-texte: #F0699E;
    }
}

:root[data-theme="sombre"] {
    --fond: #1B0E18;
    --surface: #2A1826;
    --bordure: #3D2438;
    --texte: #F7EAF0;
    --texte-doux: #B9A0AC;
    --accent-texte: #F0699E;
}

:root[data-theme="clair"] {
    --fond: #FDF6F8;
    --surface: #FFFFFF;
    --bordure: #F0DCE3;
    --texte: #4A2545;
    --texte-doux: #8A7078;
    --accent-texte: #C2185B;
    --accent-fond: #C2185B;
}

/* ---------------------------------------------------------- polices */
@font-face {
    font-family: "Cormorant Garamond";
    src: url("../fonts/cormorant-garamond-400.woff2") format("woff2");
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: Inter;
    src: url("../fonts/inter-400.woff2") format("woff2");
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: Inter;
    src: url("../fonts/inter-600.woff2") format("woff2");
    font-weight: 600;
    font-display: swap;
}

/* ---------------------------------------------------------- base */
*, *::before, *::after { box-sizing: border-box; }

html {
    /* `clip` plutôt que `hidden` : même effet de garde-fou contre un
       débordement latéral, sans casser `position: sticky` dans la page. */
    overflow-x: clip;
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--fond);
    color: var(--texte);
    font-family: var(--sans);
    font-size: var(--t-corps);
    line-height: 1.6;
    overflow-x: clip;
    -webkit-font-smoothing: antialiased;
    /* Le doigt qui vient de faire défiler ne doit pas laisser un rectangle
       gris sur la carte qu'il a effleurée. Le retour tactile est géré par
       `:active`, à nous. */
    -webkit-tap-highlight-color: transparent;
}

h1, h2, h3, .prix, .tete-nombre { font-family: var(--serif); font-weight: 400; }

a { color: var(--accent-texte); }

img { max-width: 100%; display: block; }

:focus-visible {
    outline: 2px solid var(--accent-texte);
    outline-offset: 3px;
    border-radius: 4px;
}

.contenu {
    max-width: 940px;
    margin: 0 auto;
    padding-inline: var(--marge-page);
}

/* ---------------------------------------------------------- en-tête */
.entete {
    background: var(--surface);
    border-bottom: 1px solid var(--bordure);
}
.entete .contenu {
    display: flex;
    align-items: center;
    gap: 12px;
    padding-block: 10px;
}
.marque {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: var(--texte);
    padding-block: 4px;
}
.marque svg { width: 34px; height: 34px; flex-shrink: 0; }
.marque span { font-family: var(--serif); font-size: 1.2rem; letter-spacing: .02em; }
.marque .deux { color: var(--accent-texte); }

.bascule {
    margin-left: auto;
    background: none;
    border: 1px solid var(--bordure);
    color: var(--texte-doux);
    border-radius: 999px;
    width: 44px;
    height: 44px;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    transition: transform .14s ease, border-color .18s ease;
}
.bascule:active { transform: scale(.92); }

@media (min-width: 620px) {
    .entete .contenu { padding-block: 14px; }
    .marque svg { width: 38px; height: 38px; }
    .marque span { font-size: 1.32rem; }
}

/* ---------------------------------------------------------- cartes */
.carte {
    background: var(--surface);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
}

/* ---------------------------------------------------------- boutons */
.bouton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 48px;
    padding: 12px 22px;
    background: var(--accent-fond);
    color: #fff;
    text-decoration: none;
    text-align: center;
    border: 0;
    border-radius: 9px;
    font-family: var(--sans);
    font-size: var(--t-corps);
    font-weight: 600;
    cursor: pointer;
    transition: transform .13s ease, background-color .18s ease;
}
.bouton--contour {
    background: transparent;
    color: var(--accent-texte);
    border: 1.5px solid var(--accent-texte);
}
.bouton:active { transform: scale(.975); }

@media (hover: hover) {
    .bouton:hover { background: var(--accent-survol); }
    .bouton--contour:hover { background: var(--accent-texte); color: #fff; }
}

/* ---------------------------------------------------------- têtes de section

   Le dispositif structurant de la page profil : un nombre, ce qu'il compte,
   puis un filet qui court jusqu'au bord. Les nombres ne numérotent rien, ils
   COMPTENT — six vues offertes, quarante-quatre sous clé, trois formules. Le
   rapport entre le premier et le second est l'argument de vente ; l'écrire en
   gros chiffres le rend lisible en une seconde de défilement. */
.bloc { margin-top: var(--espace-bloc); }

.tete-section {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}
.tete-nombre {
    font-size: var(--t-nombre);
    line-height: .82;
    color: var(--texte);
    font-variant-numeric: lining-nums tabular-nums;
}
.tete-libelle {
    white-space: nowrap;
    font-size: var(--t-mini);
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--texte-doux);
}
.tete-filet {
    flex: 1;
    height: 2px;
    min-width: 0;
    background: var(--bordure);
}

/* ---------------------------------------------------------- la part

   Une barre à deux tons qui dit quelle part d'un tout un nombre représente.
   Le même objet sert deux fois : sous les têtes de section — ce qui est montré
   contre ce qui est sous clé — et dans les formules, où il montre la part de la
   série que chaque pack ouvre. Un dispositif appris une fois, relu partout.

   La part est décorative au sens strict : elle ne dit rien que les nombres
   n'écrivent déjà en toutes lettres à côté. Rien n'est perdu si elle passe
   inaperçue, et il n'y a pas de rôle ARIA à lui donner.

   Le remplissage est un pseudo-élément et non un dégradé : c'est ce qui lui
   permet de se tracer à l'entrée dans l'écran, par une transformation que le
   compositeur sait animer sans repeindre. */
.part {
    position: relative;
    overflow: hidden;
    background: var(--bordure);
}
.part::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--part, 0%);
    background: var(--accent-texte);
}

/* Sur la carte dorée, le remplissage reprend le doré du liseré plutôt que le
   framboise : la carte garde une seule teinte d'accent. */
.pack--premium .part::before { background: var(--or); }

/* Le tracé n'a lieu que si la page est scriptée — c'est le script qui décide du
   moment. Sans lui la barre est simplement pleine à sa valeur. */
.js .part::before {
    transform: scaleX(0);
    transform-origin: left center;
}
.js .part.est-visible::before {
    transform: scaleX(1);
    transition: transform .9s cubic-bezier(.2, .7, .3, 1);
}

/* ---------------------------------------------------------- bandeau profil

   Portrait rond, pseudo, ligne de repères, description : la disposition que le
   visiteur vient de quitter sur Instagram. En colonne étroite tout s'empile et
   s'aligne à gauche ; la lecture ne coûte rien puisque l'œil connaît la forme. */
.bandeau {
    margin-top: clamp(20px, 5vw, 30px);
    padding: clamp(20px, 5.5vw, 30px);
}
.bandeau-portrait {
    width: clamp(88px, 24vw, 112px);
    height: clamp(88px, 24vw, 112px);
    border-radius: 50%;
    object-fit: cover;
    /* Anneau framboise détaché : le portrait reprend le cercle du logo. */
    box-shadow: 0 0 0 3px var(--surface), 0 0 0 4.5px var(--accent-texte);
    margin-bottom: 18px;
}
.bandeau-nom {
    font-size: var(--t-nom);
    line-height: 1.05;
    margin: 0;
}
.bandeau-meta {
    margin: 8px 0 0;
    font-size: var(--t-mini);
    font-weight: 600;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--texte-doux);
}
.bandeau-texte {
    margin: 14px 0 0;
    color: var(--texte-doux);
    max-width: 46ch;
}

/* Le compte Instagram est une preuve, pas un appel à l'action : derrière la
   page il y a un compte vivant que l'acheteur peut aller vérifier avant
   d'envoyer de l'argent. Il est donc écrit comme une signature — discret, sous
   la description — et jamais dessiné comme un bouton, qui entrerait en
   concurrence avec « Commander » à trente centimètres de là. */
.lien-ig {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: 14px;
    padding-block: 6px;
    color: var(--texte-doux);
    text-decoration: none;
    font-size: var(--t-petit);
    transition: color .18s ease;
}
.lien-ig .icone {
    width: 17px;
    height: 17px;
    flex-shrink: 0;
    transition: transform .2s cubic-bezier(.2, .7, .3, 1);
}
.lien-ig span { border-bottom: 1px solid transparent; transition: border-color .18s ease; }

@media (hover: hover) {
    .lien-ig:hover { color: var(--accent-texte); }
    .lien-ig:hover .icone { transform: rotate(-6deg) scale(1.12); }
    .lien-ig:hover span { border-bottom-color: currentColor; }
}

@media (min-width: 620px) {
    .bandeau {
        display: flex;
        align-items: flex-start;
        gap: clamp(22px, 4vw, 32px);
    }
    .bandeau-portrait { margin-bottom: 0; flex-shrink: 0; }
    .bandeau-corps { min-width: 0; }
}

/* Le bureau n'a pas de barre ancrée : l'appel à l'action prend sa place dans le
   bandeau, où il occupe la colonne vide et se voit sans défiler. */
.bandeau-action { display: none; }

@media (min-width: 780px) {
    .bandeau { align-items: stretch; }
    .bandeau-action {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        justify-content: center;
        gap: 10px;
        margin-left: auto;
        padding-left: clamp(22px, 3vw, 32px);
        border-left: 1px solid var(--bordure);
        flex-shrink: 0;
    }
    .bandeau-portrait { align-self: flex-start; }
}

/* ---------------------------------------------------------- grilles photo

   Deux colonnes sur téléphone. Quatre vignettes sur 390 px donneraient des
   timbres-poste de 80 px : on ne vend pas une photo qu'on ne peut pas voir. */
.grille-photos {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--gouttiere);
}
.vignette {
    position: relative;
    margin: 0;
    border-radius: var(--rayon-doux);
    overflow: hidden;
    background: var(--bordure);
    transition: transform .16s ease;
}
.vignette img {
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    transition: transform .35s cubic-bezier(.2, .7, .3, 1);
}
.vignette:active { transform: scale(.985); }

/* Le survol agrandit la photo DANS son cadre, qui ne bouge pas : la grille
   reste immobile, seule l'image respire. Réservé aux vues offertes — grossir un
   aperçu flou ne montrerait rien de plus, et la grille sous clé a déjà son
   propre mouvement lent. */
@media (hover: hover) {
    .grille-photos:not(.grille-photos--flou) .vignette:hover img { transform: scale(1.05); }
}

/* Le nombre de colonnes vient de la vue, calculé sur le nombre réel de photos,
   pour qu'une série ne se termine jamais par une rangée à moitié vide sous un
   titre qui l'annonce entière. Les valeurs de repli sont celles d'avant :
   sans la variable, la grille se comporte exactement comme auparavant. */
@media (min-width: 560px) {
    .grille-photos { grid-template-columns: repeat(var(--col-m, 3), minmax(0, 1fr)); }
}
@media (min-width: 860px) {
    .grille-photos { grid-template-columns: repeat(var(--col-l, 4), minmax(0, 1fr)); }
}

/* La grille sous clé n'a rien à faire lire : elle a une masse à montrer. Elle
   se resserre donc d'une colonne — trois sur téléphone au lieu de deux — pour
   que le volume de ce qui reste à acheter tienne dans le champ. */
.grille-photos--flou { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (min-width: 560px) {
    .grille-photos--flou { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 860px) {
    .grille-photos--flou { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* Au-delà d'une dizaine de photos, dérouler la grille entière ferait défiler le
   visiteur sur des centaines de rectangles flous avant d'atteindre les prix. La
   grille est donc coupée et s'efface vers le bas : le nombre exact est déjà
   écrit en tête de section, l'image ne doit plus que suggérer le reste.
   La coupe est décidée côté PHP, sur le nombre réel de photos — une grille
   courte n'est jamais tronquée, donc jamais estompée à tort.

   La coupe vaut aussi sur grand écran, où elle était auparavant levée. Une
   série de 44 photos y déroulait neuf rangées de cinq — près de deux mille
   pixels de vignettes floues entre les vues offertes et le premier prix, sans
   la barre de commande, qui est justement masquée à ces largeurs. L'étendue
   restait un argument, mais elle le payait du seul chemin vers l'achat.

   Ce que la grille démontrait par sa longueur, le filet de la tête de section
   le dit maintenant d'un trait : 44 sur 50, lisible en une seconde et sans
   défilement. L'argument étant fait plus haut, la grille peut se contenter de
   suggérer — c'est ce déplacement qui autorise la coupe, pas un gain de place. */
.grille-photos--tronquee {
    max-height: clamp(300px, 54vh, 520px);
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to bottom, #000 46%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 46%, transparent 100%);
}

/* La grille sous clé respire lentement : les vignettes sont vivantes, elles ne
   clignotent pas. Seize secondes par cycle, décalées les unes des autres, sans
   changement de couleur ni de netteté — rien qui attire l'œil par saccade. */
.grille-photos--flou .vignette img {
    transform: scale(1.02);
    animation: respire 16s ease-in-out infinite;
    animation-delay: calc(var(--rang, 0) * -2.3s);
}
@keyframes respire {
    0%, 100% { transform: scale(1.02); }
    50%      { transform: scale(1.08); }
}

/* ---------------------------------------------------------- packs */
.packs {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
}
.pack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: clamp(17px, 4.5vw, 22px);
    transition: transform .18s cubic-bezier(.2, .7, .3, 1),
                border-color .2s ease,
                box-shadow .22s ease;
}
.pack:active { transform: scale(.99); }

/* La carte survolée se soulève d'un cheveu. Trois pixels : assez pour que le
   pointeur sache où il est, trop peu pour que la grille tarifaire bouge. */
@media (hover: hover) {
    .pack:hover {
        transform: translateY(-3px);
        border-color: var(--accent-texte);
        box-shadow: 0 8px 22px -12px var(--accent-texte);
    }
    .pack--premium:hover {
        border-color: var(--or);
        box-shadow: 0 8px 22px -12px var(--or);
    }
}

/* Nom et lot à gauche, prix à droite : sur une colonne étroite, la carte tient
   en trois lignes au lieu de cinq. Le visiteur compare trois prix sans avoir à
   faire défiler entre eux. */
.pack-entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    width: 100%;
}
.pack-identite { min-width: 0; }
.pack-nom {
    font-family: var(--serif);
    font-size: var(--t-titre);
    line-height: 1.2;
}
.pack-lot {
    font-size: var(--t-petit);
    color: var(--texte-doux);
}
.pack-chiffres { text-align: right; }
.prix {
    margin: 0;
    font-size: var(--t-prix);
    line-height: 1;
    white-space: nowrap;
}

/* Les trois packs coûtent 50, 100 et 150 € pour 5, 10 et toute la série : c'est
   au prix unitaire que l'écart se voit, pas au prix affiché. Le seul chiffre de
   la carte qui plaide vraiment pour la formule haute. */
.pack-unitaire {
    margin: 5px 0 0;
    font-size: var(--t-petit);
    color: var(--texte-doux);
    white-space: nowrap;
}

/* Sur la formule haute, le prix unitaire cesse d'être une note de bas de carte :
   c'est là qu'il divise par trois, et c'est le seul endroit où il argumente.
   Il passe donc à l'encre pleine, sans changer de taille ni de couleur. */
.pack--premium .pack-unitaire {
    color: var(--texte);
    font-weight: 600;
}

/* La part de la série que la formule ouvre. Pleine largeur sous l'en-tête :
   les trois barres se comparent d'une carte à l'autre comme les trois prix.

   La piste est tirée de l'encre, pas de `--bordure` : sur une carte blanche, le
   rose très pâle des séparateurs disparaissait et il ne restait qu'un tiret
   framboise flottant, illisible comme proportion. Une piste qu'on ne voit pas
   ne mesure rien. */
.pack-jauge {
    width: 100%;
    height: 4px;
    border-radius: 2px;
    /* Serrée sous les chiffres qu'elle illustre, et non flottant à mi-chemin du
       bouton : elle appartient au lot, pas à l'action. */
    margin-top: 14px;
    margin-bottom: 10px;
    background: color-mix(in srgb, var(--texte) 14%, transparent);
}

.pack .bouton {
    width: 100%;
    margin-top: 15px;
}

.pack--premium {
    border-color: var(--or);
    border-width: 1.5px;
    /* Un souffle de doré, calculé depuis la surface : la distinction du pack à
       150 € tient sans ajouter une teinte de plus à la palette. */
    background: color-mix(in srgb, var(--or) 7%, var(--surface));
}
.etiquette {
    margin-bottom: 10px;
    background: var(--or);
    color: var(--or-encre);
    font-size: var(--t-mini);
    letter-spacing: .1em;
    text-transform: uppercase;
    padding: 4px 10px;
    border-radius: 4px;
    font-weight: 700;
}

/* Empilées, les cartes n'ont rien à aligner entre elles : l'étiquette vide des
   deux premières ne ferait qu'ajouter un blanc avant chaque nom. */
.etiquette:empty { display: none; }

/* Dès que trois colonnes tiennent, les formules se comparent d'un coup d'œil.
   Le seuil est celui de la barre de commande, qui disparaît au même pixel : la
   barre n'existe que tant que les trois prix ne sont pas visibles ensemble, et
   c'est ici exactement que ça devient vrai. Plus bas — le seuil était à 660 px —
   les trois cartes étaient si étroites que « Toutes les photos » passait à la
   ligne dans la seule troisième, et décalait son prix vers le bas. */
@media (min-width: 780px) {
    .packs { grid-template-columns: repeat(3, minmax(0, 1fr)); }

    /* Côte à côte, l'étiquette vide reprend sa place : elle réserve sur les
       deux premières cartes la hauteur qu'occupe « Toute la série » sur la
       troisième. Sans elle, les trois noms et les trois prix se lisaient sur
       deux lignes de base différentes — dans une grille tarifaire, c'est
       l'alignement qui rend la comparaison possible. */
    .etiquette:empty {
        display: inline-block;
        visibility: hidden;
    }
    .etiquette:empty::before { content: "\00a0"; }

    /* Le bouton se cale en bas de chaque carte : les trois s'alignent même si
       un intitulé de lot passe à la ligne. C'est lui qui absorbe désormais le
       blanc de la carte — la jauge, elle, reste collée à ses chiffres. */
    .pack .bouton { margin-top: auto; }
}

.mention {
    font-size: var(--t-petit);
    color: var(--texte-doux);
    margin: 16px 0 0;
    max-width: 62ch;
}

/* ---------------------------------------------------------- barre de commande

   La conversion quitte le site : tout le parcours tient dans un seul geste du
   pouce. La barre s'installe une fois le bandeau dépassé — le visiteur a
   commencé à regarder — et s'efface dès que les vraies formules sont à l'écran,
   pour ne pas masquer les prix qu'elle annonce. Sans JavaScript, elle reste
   simplement affichée. */
.barre-commande {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 10px var(--marge-page);
    padding-bottom: calc(10px + env(safe-area-inset-bottom));
    background: var(--surface);
    background: color-mix(in srgb, var(--surface) 93%, transparent);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    border-top: 1px solid var(--bordure);
    transition: transform .28s cubic-bezier(.2, .7, .3, 1);
}
.barre-intitule {
    display: block;
    font-size: var(--t-mini);
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--texte-doux);
}
.barre-prix {
    display: block;
    font-family: var(--serif);
    font-size: 1.45rem;
    line-height: 1.1;
    color: var(--texte);
}
.barre-commande .bouton { margin-left: auto; }

.a-barre { padding-bottom: calc(var(--hauteur-barre) + env(safe-area-inset-bottom)); }

/* Au-delà du téléphone, les trois boutons des formules sont atteignables sans
   effort : la barre n'a plus de raison d'être et disparaît. */
@media (min-width: 780px) {
    .barre-commande { display: none; }
    .a-barre { padding-bottom: 0; }
}

/* ---------------------------------------------------------- accueil */
.accueil-entete {
    padding-block: clamp(34px, 9vw, 62px) clamp(24px, 6vw, 38px);
}
.accueil-titre {
    margin: 0;
    font-size: var(--t-geant);
    line-height: 1.08;
    max-width: 14ch;
    /* Sans équilibrage, la coupe tombait après « en » et détachait le mot du
       groupe framboise qu'il ouvre. Les navigateurs qui ignorent la propriété
       reviennent simplement au comportement actuel. */
    text-wrap: balance;
}
.accueil-titre .souligne { color: var(--accent-texte); }
.accueil-intro {
    margin: 16px 0 0;
    color: var(--texte-doux);
    max-width: 42ch;
}

/* Dès que la place existe, le titre et la phrase de positionnement se partagent
   la largeur, calés sur la même ligne de pied. Empilés, ils laissaient la
   moitié droite de l'écran vide sans que ce vide dise quoi que ce soit. */
@media (min-width: 820px) {
    .accueil-entete {
        display: grid;
        grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
        gap: clamp(30px, 5vw, 60px);
        align-items: end;
    }
    .accueil-titre { max-width: none; }
    .accueil-intro { margin: 0 0 10px; }
}

.cartes-profils {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(16px, 4vw, 22px);
}
.carte-profil {
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    display: flex;
    flex-direction: column;
    transition: transform .2s cubic-bezier(.2, .7, .3, 1),
                border-color .2s ease,
                box-shadow .25s ease;
}
.carte-profil:active { transform: scale(.99); }

/* Le cadre porte le débordement pour que l'image puisse grossir à l'intérieur
   sans que la carte change de taille. */
.carte-profil-image {
    position: relative;
    overflow: hidden;
}
.carte-profil img {
    width: 100%;
    height: auto;
    /* Portrait, comme la photo derrière. En 4/3 le recadrage d'un visage
       vertical lui coupait le haut du crâne, et jetait les deux tiers du
       fichier — sur une page qui ne vend que des photos. */
    aspect-ratio: 4 / 5;
    object-fit: cover;
    object-position: center 22%;
    transition: transform .5s cubic-bezier(.2, .7, .3, 1);
}

/* Le compte de photos, posé sur l'image : la même chose que le nombre des têtes
   de section, à l'échelle d'une vignette de catalogue. */
.carte-profil-compte {
    position: absolute;
    left: 12px;
    bottom: 12px;
    padding: 5px 10px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    color: var(--texte);
    font-size: var(--t-mini);
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.carte-profil-corps {
    padding: clamp(16px, 4.5vw, 20px);
    display: flex;
    flex-direction: column;
    flex: 1;
}
.carte-profil-nom {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    font-family: var(--serif);
    font-size: var(--t-titre);
}
.carte-profil-nom .voir {
    font-family: var(--sans);
    font-size: var(--t-mini);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--accent-texte);
    white-space: nowrap;
}
.carte-profil-accroche {
    /* La marge basse garantit l'écart minimal avec le filet du prix, que la
       carte ait du blanc à distribuer ou non. */
    margin: 8px 0 16px;
    font-size: var(--t-petit);
    color: var(--texte-doux);
}

/* Le prix d'entrée ferme la carte, calé en bas quelle que soit la longueur de
   l'accroche : les deux cartes se lisent alors sur la même ligne. */
.carte-profil-prix {
    /* `auto` en haut : le prix tombe au bas de la carte, donc à la même hauteur
       sur les deux, que l'accroche tienne sur une ligne ou deux. */
    margin: auto 0 0;
    padding-top: 14px;
    border-top: 1px solid var(--bordure);
    font-size: var(--t-petit);
    color: var(--texte-doux);
}
.carte-profil-prix strong {
    font-family: var(--serif);
    font-size: 1.2rem;
    font-weight: 400;
    color: var(--texte);
}
.carte-profil-nom .voir::after {
    content: " →";
    display: inline-block;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1);
}

@media (min-width: 620px) {
    .cartes-profils { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (hover: hover) {
    .carte-profil:hover {
        border-color: var(--accent-texte);
        transform: translateY(-4px);
        box-shadow: 0 14px 30px -18px var(--accent-texte);
    }
    .carte-profil:hover img { transform: scale(1.045); }
    .carte-profil:hover .voir::after { transform: translateX(4px); }
}

/* ---------------------------------------------------------- pages simples */
.page-simple { margin-top: clamp(28px, 7vw, 48px); max-width: 620px; }
.page-simple h1 { font-size: var(--t-geant); line-height: 1.1; margin: 0 0 18px; }
.page-simple p { color: var(--texte-doux); }
.page-simple .bouton { margin-top: 6px; }

.page-texte { margin-top: clamp(28px, 7vw, 44px); max-width: 68ch; }
.page-texte h1 { font-size: clamp(1.9rem, 1.4rem + 2.2vw, 2.6rem); line-height: 1.12; margin: 0 0 20px; }
.page-texte h2 { font-size: var(--t-titre); margin: 30px 0 8px; }

.carte-commande { padding: clamp(20px, 5.5vw, 26px); margin-top: 22px; max-width: 520px; }
.commande-sujet { margin: 0; color: var(--texte-doux); }
.carte-commande .bouton { width: 100%; margin-top: 18px; }
.etat-commande { font-size: var(--t-petit); color: var(--texte-doux); margin: 14px 0 0; }

/* ---------------------------------------------------------- pied */
.pied {
    background: var(--surface);
    border-top: 1px solid var(--bordure);
    margin-top: var(--espace-bloc);
    padding-block: 18px;
    font-size: var(--t-petit);
    color: var(--texte-doux);
}
.pied .contenu {
    display: flex;
    gap: 6px 18px;
    flex-wrap: wrap;
    align-items: center;
}
.pied a {
    color: var(--texte-doux);
    /* Espacement vertical suffisant pour que deux liens voisins ne se
       chevauchent pas sous le pouce quand ils passent à la ligne. */
    padding-block: 10px;
}
.pied .courriel { padding-block: 10px; }
.majeur {
    border: 1px solid var(--accent-texte);
    color: var(--accent-texte);
    border-radius: 3px;
    padding: 2px 7px;
    font-weight: 700;
}

/* ---------------------------------------------------------- animations

   Tout ce qui bouge est ici, et tout ce qui est ici s'éteint plus bas sous
   `prefers-reduced-motion`. Les états initiaux masqués sont conditionnés à la
   classe `.js`, posée par `theme.js` avant le premier rendu : sans JavaScript,
   rien n'est jamais caché. */
.js .apparait {
    opacity: 0;
    transform: translateY(14px);
}
.js .apparait.est-visible {
    opacity: 1;
    transform: none;
    transition: opacity .5s ease, transform .5s cubic-bezier(.2, .7, .3, 1);
    transition-delay: calc(var(--rang, 0) * 60ms);
}

.js .barre-commande { transform: translateY(130%); }
.js .barre-commande.est-montree { transform: none; }

@keyframes monte {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}
.js .bandeau-portrait { animation: monte .5s cubic-bezier(.2, .7, .3, 1) both; }
.js .bandeau-nom      { animation: monte .5s .07s cubic-bezier(.2, .7, .3, 1) both; }
.js .bandeau-meta     { animation: monte .5s .13s cubic-bezier(.2, .7, .3, 1) both; }
.js .bandeau-texte    { animation: monte .5s .19s cubic-bezier(.2, .7, .3, 1) both; }
.js .lien-ig          { animation: monte .5s .25s cubic-bezier(.2, .7, .3, 1) both; }

/* L'accueil s'ouvre dans l'ordre où il se lit : la phrase, puis ce qu'elle
   promet, puis le compte des profils. Les cartes suivent par leur propre
   apparition au défilement — elles sont déjà à l'écran, l'enchaînement se
   poursuit sans rupture. */
.js .accueil-titre { animation: monte .55s cubic-bezier(.2, .7, .3, 1) both; }
.js .accueil-intro { animation: monte .55s .1s cubic-bezier(.2, .7, .3, 1) both; }
.js .cartes-profils + *,
.js .accueil-entete + .tete-section { animation: monte .55s .18s cubic-bezier(.2, .7, .3, 1) both; }

/* Troubles vestibulaires : une animation de fond peut provoquer un vrai malaise,
   pas une gêne esthétique. On coupe tout, sans exception. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    .js .apparait,
    .js .apparait.est-visible {
        opacity: 1 !important;
        transform: none !important;
    }
    .grille-photos--flou .vignette img { transform: none !important; }

    /* `monte` part d'une opacité nulle : réduire sa durée ne suffit pas, il
       faut la supprimer. Tant qu'elle reste déclarée, le pseudo, l'âge, la
       ville et la description du profil n'existent à l'écran qu'une fois
       l'animation jouée — le nom de la personne dont on vend les photos est le
       dernier élément de la page qui devrait dépendre de ça. */
    .js .bandeau-portrait,
    .js .bandeau-nom,
    .js .bandeau-meta,
    .js .bandeau-texte,
    .js .lien-ig,
    .js .accueil-titre,
    .js .accueil-intro,
    .js .accueil-entete + .tete-section { animation: none !important; }

    /* Même raison pour la part : son remplissage part d'une échelle nulle.
       Réduire la durée le laisserait invisible le temps d'une image. */
    .js .part::before,
    .js .part.est-visible::before {
        transform: none !important;
        transition: none !important;
    }
}
