/*
Theme Name: Kim B Lucas — Le Viseur
Theme URI: https://kimblucas.com/
Author: Yan Haltey Brands
Description: Le portfolio de Kim B Lucas — une galerie plein écran conçue comme un viseur, et la boutique de tirages qui la prolonge. ⚠ FICHIER GÉNÉRÉ depuis sites/kimblucas/prototype/ : toute retouche faite directement sur le serveur est perdue à la génération suivante, et n'existe alors plus nulle part.
Version: 0.9.0
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 8.1
Text Domain: kimb-native
Tags: portfolio, photography, e-commerce, full-width-template, custom-colors, translation-ready
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/

/* ---- fonts.css, chemins reecrits depuis la racine du theme ---- */
/* Polices auto-hébergées — plus aucune requête vers Google au chargement.
   Sous-ensembles latin et latin-ext uniquement. Régénérer : voir README du prototype. */
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(assets/fonts/InstrumentSans-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(assets/fonts/InstrumentSans-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(assets/fonts/InstrumentSans-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(assets/fonts/InstrumentSans-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(assets/fonts/InstrumentSans-600-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(assets/fonts/InstrumentSans-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Libre Caslon Display';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/LibreCaslonDisplay-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Libre Caslon Display';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/LibreCaslonDisplay-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/SplineSansMono-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(assets/fonts/SplineSansMono-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(assets/fonts/SplineSansMono-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Spline Sans Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(assets/fonts/SplineSansMono-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ---- viseur.css, repris tel quel ---- */
/* ============================================================
   Le Viseur — prototype vanilla · Kim B Lucas · v6
   HUD qui s'efface pour contempler · acquérir bas-centre ·
   menu LCD (+ Instagram valorisé) · descriptions série + image (Info).
   ============================================================ */

:root {
  --noir:#050505; --surface:#171715; --surface-2:#1c1c1a; --line:#2a2a28;

  /* ⚠️ LA BORDURE D'UN CHAMP N'EST PAS UN FILET DE MISE EN PAGE — mesuré le 23.08.2026.
     `--line` (#2a2a28) sépare des blocs sur du noir ; à ce titre il n'a aucun seuil à tenir.
     Posé sur un champ de saisie, il devient la seule chose qui dit OÙ commence le champ, et il
     tombe alors sous le critère 1.4.11 : 3:1 minimum contre le fond adjacent.

         #2a2a28 sur #171715 (fond du champ)  →  1,25:1     l'audit relève 1,63:1
         #2a2a28 sur #050505 (fond de page)   →  1,42:1
         #6b6b67 sur #171715                  →  3,35:1  ✓
         #6b6b67 sur #050505                  →  3,81:1  ✓

     ⚠️ C'est un jeton SÉPARÉ, et non `--line` relevé : `--line` habille une trentaine de
     séparateurs dont l'éclaircir changerait le dessin de tout le site. Yan a déjà refusé deux
     fois qu'on touche à la palette. On ne relève que ce qui doit passer un seuil. */
  --bord-champ:#6b6b67;
  --gris:#7a7a76; --gris-2:#a6a39d; --ambre:#e8c79a; --vert:#7cc08a;
  --rouge:#e4483b; --blanc:#f5f4f1;
  --ease:cubic-bezier(0.65,0.05,0,1); --focus-dur:0.9s;
  --hud-pad:clamp(0.9rem,2.2vw,1.75rem);
  /* Respiration entre le cadre de visée et ce qui s'écrit dedans. Horizontale plus généreuse que
     verticale : l'œil accepte un texte proche du bord haut ou bas, il supporte mal qu'il touche
     le bord latéral. Une seule paire de valeurs pour TOUS les éléments internes — sans ça,
     chacun dérive et le cadre perd son aplomb. */
  --hud-in-x:clamp(1.1rem,2.2vw,2.1rem);
  --hud-in-y:clamp(0.7rem,1.3vw,1.25rem);
  --dock-w:384px;  /* panneau latéral : colonne séries + pellicule.
                      308 px étaient trop étroits une fois la règle d'édition et les badges
                      remontés dedans : le texte tombait à trois mots par ligne. */
  --dock-lift:0px; /* hérité de la galerie en bas — vaut 0 depuis le passage en latéral */
  --dock-shift:0px;/* décalage du cartel quand la galerie est ouverte */
  --mono:"Spline Sans Mono",ui-monospace,"SFMono-Regular",Menlo,monospace;
  --serif:"Libre Caslon Display",Georgia,"Times New Roman",serif;
  --sans:"Instrument Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
}
* { box-sizing:border-box; margin:0; padding:0; }
html,body { background:var(--noir); color:var(--blanc); font-family:var(--sans); }
body { min-height:100vh; min-height:100dvh; display:flex; flex-direction:column; }
img { display:block; max-width:100%; }
a { color:inherit; text-decoration:none; }
[hidden] { display:none !important; }

/* ---------- Intro cinématique (flou → net, on tient ~5 s, sortie zoom + fondu ; JS pilote la sortie) ---------- */
/* Les styles de l'intro-portrait ont été retirés le 03.08.2026 avec son balisage : un jeu
   de règles qui ne s'applique à rien finit toujours par être recopié ailleurs par erreur. */

/* ---------- « Découvrir Kim » — récit à la demande (séquence de photos + lignes) ---------- */
.story { position:fixed; inset:0; z-index:100; background:#000; overflow:hidden; transition:opacity 0.9s var(--ease); }
.story.is-leaving { opacity:0; }
/* #113 — l'image s'affiche ENTIERE, jamais recadree par defaut.
   Le diaporama etait en `cover` : sur un ecran d'un ratio different du fichier, il rognait la
   photographie sans que personne l'ait demande. Le choix portrait/paysage selon l'orientation de
   la fenetre RESTE, et devient ce qu'il aurait toujours du etre — une optimisation du cadrage,
   pas un rattrapage de recadrage.
   Consequence assumee : l'introduction n'est plus plein cadre, elle peut porter des bandes. */
.story__img { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; transform-origin:center;
  opacity:0; filter:blur(24px) saturate(.5) brightness(1.06); transform:scale(1.0); will-change:opacity,filter,transform;
  transition:opacity 0.9s var(--ease), filter 1.4s var(--ease), transform 7.5s linear;
  backface-visibility:hidden; }
.story__img.show { opacity:1; filter:blur(0) saturate(1) brightness(1); transform:scale(1.08); }
/* Ken Burns : une dérive lente, de direction différente à chaque vue (classe posée en JS). */
/* Amplitudes volontairement faibles : au-delà, la dérive saccade au lieu de glisser.
   translate3d force la composition GPU (le simple translate repassait par le CPU). */
.story__img.kb0.show { transform:translate3d(-0.7%,-0.5%,0) scale(1.055); }
.story__img.kb1.show { transform:translate3d( 0.7%, 0.5%,0) scale(1.055); }
.story__img.kb2.show { transform:translate3d( 0.5%,-0.7%,0) scale(1.06);  }
.story__img.kb3.show { transform:translate3d(-0.5%, 0.7%,0) scale(1.055); }
.story__veil { position:absolute; inset:0; pointer-events:none; background:linear-gradient(transparent 42%, rgba(0,0,0,0.62)); }
.story__line { position:absolute; left:0; right:0; bottom:clamp(4.5rem,13vh,7.5rem); margin:0 auto; max-width:24ch; padding:0 1.5rem;
  text-align:center; font-family:var(--serif); font-size:clamp(1.4rem,3.6vw,2.4rem); line-height:1.25; color:var(--blanc);
  opacity:0; transform:translateY(12px); transition:opacity 0.9s var(--ease), transform 0.9s var(--ease); text-shadow:0 2px 30px rgba(0,0,0,0.65); }
.story__line.show { opacity:1; transform:none; }
/* white-space:pre-line — les phrases du diaporama portent leur propre coupure de ligne, un
   « \n » dans les données. Sans cette règle, textContent l'écrase et la phrase se recoupe où
   la largeur veut : « Je crée mon univers. J'explore, je ressens, je » puis « capture. ».
   La coupure est un choix de composition, elle appartient au texte, pas à la fenêtre. */
.story__line { white-space:pre-line; }
.story__brand { position:absolute; inset:0; display:grid; place-items:center; font-family:var(--serif);
  letter-spacing:.32em; font-size:clamp(1.4rem,3.2vw,2.2rem); color:var(--blanc); opacity:0; transition:opacity 1s var(--ease); }
.story__brand.show { opacity:.95; }
.story__foot { position:absolute; left:0; right:0; bottom:clamp(1.2rem,3vw,2rem); z-index:2;
  display:flex; align-items:center; justify-content:space-between; padding:0 clamp(1.2rem,3vw,2.4rem); }
.story__dots { display:flex; gap:8px; }
.story__dot { width:6px; height:6px; border-radius:50%; background:rgba(245,244,241,0.25); transition:background 0.4s var(--ease), transform 0.4s var(--ease); }
.story__dot.on { background:var(--ambre); transform:scale(1.3); }
.story__skip { font-family:var(--mono); font-size:0.76rem; letter-spacing:0.16em; text-transform:uppercase; color:var(--gris-2);
  background:transparent; border:0; cursor:pointer; padding:0.5rem; }
.story__skip:hover,.story__skip:focus-visible { color:var(--blanc); outline:none; }
@media (prefers-reduced-motion: reduce) {
  .story__img { transition:opacity 0.4s ease; filter:none !important; transform:none !important; }
}

/* ---------- Viseur ---------- */
.viewfinder { position:relative; flex:1 1 auto; min-height:80vh; min-height:80dvh; overflow:hidden; background:var(--noir); }
.stage { position:absolute; inset:0; overflow:hidden; }
.stage__img { width:100%; height:100%; object-fit:cover; transform-origin:center; filter:saturate(1.02) contrast(1.02); transition:filter var(--focus-dur) var(--ease),transform 0.09s linear; will-change:filter,transform; }
.viewfinder.is-focusing .stage__img { filter:blur(24px) saturate(0.25) brightness(1.12) contrast(0.96); transition:none; }
.stage__vignette { position:absolute; inset:0; pointer-events:none; transition:opacity 0.8s var(--ease);
  background:linear-gradient(to bottom,rgba(0,0,0,0.42),transparent 20% 62%,rgba(0,0,0,0.62)),
             radial-gradient(130% 100% at 50% 40%,transparent 58%,rgba(0,0,0,0.4) 100%); }

/* ---------- Chrome qui s'efface (contemplation) ---------- */
.hud__grid,.hud__corner,.reticle,.acquire,.ctl,.fitbtn,
.cartel,.bottomdock,.swipehint,.zoomhud,.hud__top { transition:opacity 0.7s var(--ease), bottom 0.45s var(--ease); }
.cartel { transition:left 0.45s var(--ease), transform 0.45s var(--ease); }
.dial { transition:opacity 0.7s var(--ease); }
body.dock-on { --dock-lift:0px; }   /* galerie latérale : plus rien ne remonte */
/* Contemplation. Restent TOUJOURS lisibles : le bandeau haut (série · compteur) et le cartel
   (titre, lieu, EXIF). Tout le reste s'efface — y compris la molette de menu.
   Le réveil ne se fait QUE sur mouvement de souris ou tap : naviguer aux flèches, à la molette
   ou en cliquant l'image laisse l'interface effacée (voir wake() dans viseur.js). */
body.idle .hud__grid,
body.idle .hud__corner,
body.idle .reticle,
body.idle .acquire,
body.idle .ctl,
body.idle .fitbtn,
body.idle .dial,
/* #126 — a l'inactivite, TOUS les textes du viseur s'effacent.
   Le mode contemplation existait mais ne touchait que les commandes : le cartel (titre, lieu,
   phrase d'auteur), la pellicule et la colonne des series restaient a l'ecran, c'est-a-dire
   exactement le texte qu'on voulait faire disparaitre. La photographie se retrouve seule. */
body.idle .cartel,
body.idle .swipehint,
body.idle .hud__top,
body.idle .zoomhud { opacity:0; pointer-events:none; }
/* `.hud__top` — le compteur et la signature — a rejoint cette liste le 23.08.2026, à la demande
   de Yan. Le commentaire ci-dessus disait « restent TOUJOURS lisibles : le bandeau haut et le
   cartel » ; le cartel en est sorti au ticket #126, le bandeau restait seul à contredire la
   règle. À l'inactivité, la photographie est maintenant réellement seule. */
/* `.bottomdock` N'EST PAS dans cette liste, et c'est la décision du 11.08 :
   « le panneau lui-même reste visible — le visiteur vient de l'ouvrir
   volontairement, et masquer ce que quelqu'un vient de demander à voir est un
   défaut, pas une fonction. »

   Il y figurait. Le comportement était juste quand même, mais par ACCIDENT :
   `body.dock-on .bottomdock { opacity:1 }` vit 300 lignes plus bas, à
   spécificité égale, et ne l'emportait que par l'ordre du fichier. Déplacer un
   bloc aurait fait disparaître le panneau à l'inactivité — précisément ce que
   le ticket interdit, et sans que rien ne relie la cause à l'effet.

   Fermé, `.bottomdock` est déjà à `opacity:0` par défaut : le retirer d'ici ne
   change donc rien au rendu, seulement la raison pour laquelle il est juste. */
body.idle .stage__vignette { opacity:0.35; }
/* (Le retour du cadre de visée avec la barre du bas est géré plus bas, avec .hud__grid.) */

/* ---------- Cadre viseur ---------- */
.hud { position:absolute; inset:0; padding:var(--hud-pad); font-family:var(--mono);
  font-size:clamp(0.75rem,1.15vw,0.8rem); letter-spacing:0.08em; color:var(--gris-2);
  text-transform:uppercase; pointer-events:none; }
.dim { color:var(--gris); }
/* Le cadre de visée épouse la PHOTO, pas la fenêtre : --frame-* est recalculé en JS à chaque
   changement d'image, de mode d'affichage ou de taille de fenêtre. Sans ça, depuis que l'image
   s'affiche en entier, le quadrillage débordait sur les bandes noires. */
.hud__grid { position:absolute; pointer-events:none; opacity:0.4;
  left:var(--frame-l,var(--hud-pad)); top:var(--frame-t,var(--hud-pad));
  width:var(--frame-w,auto); height:var(--frame-h,auto);
  background:linear-gradient(var(--line),var(--line)) 33.33% 0 / 1px 100% no-repeat,
            linear-gradient(var(--line),var(--line)) 66.66% 0 / 1px 100% no-repeat,
            linear-gradient(var(--line),var(--line)) 0 33.33% / 100% 1px no-repeat,
            linear-gradient(var(--line),var(--line)) 0 66.66% / 100% 1px no-repeat; }
.hud__corner { position:absolute; width:22px; height:22px; border:1.5px solid var(--blanc); opacity:0.8; }
.hud__corner.tl { top:var(--frame-t,var(--hud-pad)); left:var(--frame-l,var(--hud-pad)); border-right:0; border-bottom:0; }
.hud__corner.tr { top:var(--frame-t,var(--hud-pad)); left:calc(var(--frame-l,var(--hud-pad)) + var(--frame-w,0px) - 22px); border-left:0; border-bottom:0; }
.hud__corner.bl { top:calc(var(--frame-t,var(--hud-pad)) + var(--frame-h,0px) - 22px); left:var(--frame-l,var(--hud-pad)); border-right:0; border-top:0; }
.hud__corner.br { top:calc(var(--frame-t,var(--hud-pad)) + var(--frame-h,0px) - 22px); left:calc(var(--frame-l,var(--hud-pad)) + var(--frame-w,0px) - 22px); border-left:0; border-top:0; }
/* Le bandeau haut se cale sur la PHOTO (--frame-*), plus sur la fenêtre.
   Il était positionné par rapport au bord de l'écran, alors que les coins de visée et le
   quadrillage le sont par rapport à l'image. En « image entière », la photo est en boîte à
   lettres : le texte commençait donc dans la bande noire et finissait sur la photo, à cheval sur
   l'arête. Deux repères différents dans un même cadre, ça se voit toujours. */
.hud__top { position:absolute;
  top:calc(var(--frame-t, var(--hud-pad)) + var(--hud-in-y));
  left:calc(var(--frame-l, var(--hud-pad)) + var(--hud-in-x));
  width:calc(var(--frame-w, 100%) - 2 * var(--hud-in-x));
  display:flex; justify-content:space-between; align-items:flex-start; gap:1rem; padding:0; }
/* Signature de marque, en haut à droite. Plus petite que le titre d'une vue, jamais en
   concurrence avec la photo. Le compteur passe au-dessus, la signature dessous. */
.hud__topR { display:flex; flex-direction:column; align-items:flex-end; gap:0.28rem; text-align:right; }
.wordmark { font-family:var(--serif); text-transform:none; letter-spacing:0.2em;
  font-size:clamp(0.75rem,1.15vw,0.82rem); color:var(--blanc); opacity:0.7; white-space:nowrap; }
.reticle { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); display:grid; place-items:center; color:var(--vert); }
.reticle__mark { font-size:clamp(1.4rem,3vw,2rem); line-height:1; }
.reticle__label { position:absolute; top:calc(50% + 1.4rem); font-size:0.8rem; letter-spacing:0.2em; }
.reticle[data-state="focus"] { color:var(--ambre); }
.reticle[data-state="focus"] .reticle__mark { animation:hintpulse 0.5s ease-in-out infinite; }
.af { position:absolute; border:1.5px solid currentColor; opacity:0.9; }
.af--l { left:-34px; width:2px; height:18px; border-right:0; border-top:0; border-bottom:0; }
.af--r { right:-34px; width:2px; height:18px; border-left:0; border-top:0; border-bottom:0; }
.af--t { top:-30px; height:2px; width:18px; border-bottom:0; border-left:0; border-right:0; }
.af--b { bottom:-30px; height:2px; width:18px; border-top:0; border-left:0; border-right:0; }
@keyframes hintpulse { 0%,100%{opacity:0.5;} 50%{opacity:0.95;} }

/* Le « viseur » — cadre (coins), quadrillage et réticule — n'apparaît QU'AVEC la barre du bas
   (souris en zone basse / tap), pas à chaque navigation : moins fatigant, plus immersif.
   Le titre, « Acquérir » et le menu, eux, restent visibles pendant qu'on navigue. */
/* L'habillage — quadrillage, coins, réticule — ne dépend PLUS de la galerie.
   Il en dépendait, et sur mobile la galerie s'ouvre en plein écran : l'habillage était donc
   allumé mais recouvert. Structurellement invisible au téléphone, sans que rien ne le signale.
   Deux régimes, un par famille d'appareils, et rien d'autre :
     · avec une souris, l'habillage suit l'activité et s'efface à l'inactivité ;
     · au doigt, il se montre à l'appui et se cache au suivant. */
.hud__grid, .hud__corner, .reticle { opacity:0; transition:opacity 0.5s var(--ease); }

@media (hover: hover) {
  body:not(.idle) .hud__grid   { opacity:0.4; }
  body:not(.idle) .hud__corner { opacity:0.8; }
  body:not(.idle) .reticle     { opacity:1; }
}
@media (hover: none) {
  body.habillage .hud__grid   { opacity:0.4; }
  body.habillage .hud__corner { opacity:0.8; }
  body.habillage .reticle     { opacity:1; }
}

/* ---------- Le texte quitte la photo, au doigt ----------
   Le cartel — phrase, lieu, titre, données de prise de vue — se posait SUR l'image en permanence.
   Sur un site de photographie, c'est le sujet qu'on abîme. Au doigt, il ne s'affiche donc plus
   qu'avec l'habillage, par le même appui que le quadrillage : au repos on ne voit que l'image.
   Avec une souris, il reste comme avant, il s'efface à l'inactivité. */
@media (hover: none) {
  .cartel { opacity:0; transition:opacity 0.45s var(--ease); }
  body.habillage .cartel { opacity:1; }
}

/* ---------- chevrons d'aide au glissement ----------
   Discrets, centrés sur la hauteur, retirés au premier geste. Ils ne captent aucun pointeur :
   le glissement doit traverser jusqu'à la surface de geste. */
.swipehint { position:absolute; z-index:3; left:0; right:0; top:50%; transform:translateY(-50%);
  display:flex; align-items:center; justify-content:center; gap:1.1rem;
  pointer-events:none; opacity:0; transition:opacity 0.8s var(--ease); }
.swipehint.on { opacity:1; }
.swipehint__ch { font-family:var(--serif); font-size:1.9rem; line-height:1; color:var(--blanc);
  opacity:0.5; text-shadow:0 2px 14px rgba(0,0,0,0.75); }
.swipehint__ch--l { animation:chevgauche 2.6s var(--ease) infinite; }
.swipehint__ch--r { animation:chevdroite 2.6s var(--ease) infinite; }
.swipehint__lbl { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.18em;
  text-transform:uppercase; color:var(--blanc); opacity:0.62;
  text-shadow:0 2px 14px rgba(0,0,0,0.8); }
@keyframes chevgauche { 0%,100%{transform:translateX(0);opacity:0.5;} 50%{transform:translateX(-5px);opacity:0.85;} }
@keyframes chevdroite { 0%,100%{transform:translateX(0);opacity:0.5;} 50%{transform:translateX(5px);opacity:0.85;} }
/* Une aide qui bouge chez qui a demandé moins de mouvement, c'est exactement ce qu'on lui épargne. */
@media (prefers-reduced-motion: reduce) {
  .swipehint__ch--l, .swipehint__ch--r { animation:none; }
}
/* Sans tactile, le glissement n'est pas le geste naturel : la molette et les flèches le sont. */
@media (hover: hover) { .swipehint { display:none; } }


/* Focale simulée (au zoom) */
.zoomhud { position:absolute; top:calc(var(--hud-pad) + 2.7rem); left:50%; z-index:4;
  display:inline-flex; align-items:baseline; gap:0.5rem; font-family:var(--mono);
  padding:0.3rem 0.75rem; border:1px solid rgba(232,199,154,0.4); border-radius:2px;
  background:rgba(8,8,7,0.5); backdrop-filter:blur(6px);
  opacity:0; transform:translate(-50%,-6px); transition:opacity 0.3s var(--ease),transform 0.3s var(--ease); pointer-events:none; }
.viewfinder.zoomed .zoomhud { opacity:1; transform:translate(-50%,0); }
.zoomhud__focal { color:var(--ambre); font-size:0.92rem; letter-spacing:0.06em; }
.zoomhud__x { color:var(--gris-2); font-size:0.76rem; letter-spacing:0.1em; }

/* ---------- Cartel (bas-gauche) ---------- */
/* pointer-events:none — le cartel est posé AU-DESSUS de la surface de geste (z-index 3 contre 1).
   Sur un écran court, iPhone SE ou 320 px, il remonte jusqu'au milieu de l'écran et occupe près
   d'un quart de la hauteur : le doigt qui se pose là, l'endroit le plus naturel, ne rencontrait
   pas le viseur mais le texte, et le glissement ne faisait rien. Le cartel ne contient aucun
   élément cliquable, uniquement du texte, donc le laisser traverser ne coûte rien.
   Si un lien y est ajouté un jour, il faudra lui redonner `pointer-events:auto`. */
.cartel { position:absolute; left:calc(var(--frame-l, var(--hud-pad)) + var(--hud-in-x)); /* Ancré au bas de la PHOTO, comme le reste du cadre. Il l'était à la fenêtre : en boîte à
     lettres, le cartel se retrouvait dans la bande noire, sous l'image. */
  /* +1.9rem : la signature « © Kim B Lucas » occupe désormais la ligne du bas à gauche.
     Le cartel se pose au-dessus d'elle, sinon la ligne de prise de vue l'effleure. */
  bottom:calc(100% - var(--frame-t, 0px) - var(--frame-h, 100%) + var(--hud-in-y) + 1.9rem); max-width:min(52ch,56%); z-index:3;
  pointer-events:none;
  transition:left 0.45s var(--ease); }
/* Le cartel est calé sur --frame-l, c'est-à-dire sur la PHOTO. Bien pour la boîte à lettres,
   insuffisant quand le panneau des séries est ouvert.

   ⚠️ L'erreur qui était écrite ici : « la photo se décale d'elle-même quand le panneau s'ouvre ».
   Elle ne se décale pas. `syncFrame()` calcule --frame-l à partir de la seule largeur du viseur
   (js/viseur.js) et ignore le panneau, qui est un CALQUE FIXE posé par-dessus. En plein cadre,
   --frame-l vaut le padding — une trentaine de pixels — donc le titre se retrouvait entièrement
   masqué derrière les 384 px du panneau. Le bouton « Séries » avait reçu ce décalage le 05.08,
   le cartel avait été oublié : même cause, deux endroits, un seul corrigé.

   On ne double PAS le décalage : --frame-l ne bouge pas, on part du bord du panneau et non de la
   somme des deux — c'est ce doublement qui, la première fois, envoyait le titre percuter le
   bouton d'achat au centre.

   Borné à l'écran large : SOUS 680 px le panneau passe en plein écran (left:0; right:0;
   width:auto) et --dock-w ne veut plus rien dire. Sans cette borne on poussait le titre 384 px
   à droite d'un écran qui en fait 390 — donc dehors. */
@media (min-width: 681px) {
  /* ⚠️ CETTE RÈGLE NE GAGNAIT PAS, ET LA SPÉCIFICITÉ N'Y ÉTAIT POUR RIEN.
     Mesuré en production le 21.08.2026, panneau ouvert, interface réveillée :
     `body.dock-on` présent, le média correspond, aucune règle concurrente ne pose
     `left`, aucun `!important`, aucun style en ligne — et le cartel restait à
     61,6 px, c'est-à-dire à la valeur de la règle de base. Sur un `.cartel` neuf
     créé dans le même document, la même règle rendait bien 417,6 px.
     Je n'ai pas su expliquer l'écart, et une règle qu'on ne sait pas expliquer
     n'est pas une règle sur laquelle on s'appuie. On change donc de mécanisme :
     le décalage passe par une VARIABLE posée sur `body`, que la règle de base
     lit. Une propriété personnalisée hérite — elle ne se dispute pas la cascade,
     et il n'y a plus qu'un seul endroit qui pose `left` sur cet élément. */
  /* ⚠️ LE DÉCALAGE PASSE PAR UNE TRANSFORMATION, ET CE N'EST PAS UN CAPRICE.
     Trois mécanismes ont été essayés sur cet élément, mesurés en production le
     21.08.2026, panneau ouvert et interface réveillée :
       `body.dock-on .cartel { left: … }`   → sans effet (left reste à 61,6 px)
       une variable héritée posée sur `body`  → sans effet non plus, alors
                                              qu'elle valait bien 384px SUR l'élément
       `element.style.left = 400px !important` → sans effet non plus
     `left` est donc inopérant sur ce cartel, pour une raison que je n'ai pas
     élucidée — `inset` le rend pourtant (61,6 px), la règle est servie, il n'y
     a ni !important concurrent ni style en ligne.
     `margin-left` et `translateX` déplacent l'élément tous les deux : mesuré
     62 px → 446 px avec l'un comme avec l'autre. On retient `translateX`, qui
     ne touche pas à la mise en page et s'anime avec la transition.
     La borne des 681 px reste : sous 680 px le panneau passe en plein écran et
     `--dock-w` ne veut plus rien dire — pousser le titre de 384 px sur un écran
     qui en fait 390 est le second piège que #114 nomme. */
  body.dock-on .cartel { transform:translateX(var(--dock-w, 384px)); }
}
/* La phrase de Kim est TOUJOURS là : elle fait partie du cartel, au même titre que le titre et le
   lieu. Le bouton « Info » qui la dépliait est retiré (2026-07-29) — il n'y avait plus rien à
   déplier. (classe renommée : « story » entrait en collision avec l'overlay .story, inset:0) */
.cartel__story { font-family:var(--serif); font-style:italic; font-size:clamp(0.95rem,1.6vw,1.15rem); color:var(--blanc); max-width:40ch; margin-bottom:0.7rem; opacity:0.92; text-shadow:0 2px 20px rgba(0,0,0,0.7); }
.cartel__idx { font-family:var(--mono); color:var(--ambre); font-size:0.8rem; letter-spacing:0.16em; margin-bottom:0.35rem; text-transform:uppercase; }
.cartel__title { font-family:var(--serif); font-weight:400; font-size:clamp(1.8rem,5vw,3.4rem); line-height:1.02; color:var(--blanc); text-shadow:0 2px 30px rgba(0,0,0,0.6); }
.cartel__meta { display:flex; align-items:center; gap:1rem; margin-top:0.7rem; flex-wrap:wrap; }
/* Une seule couleur pour toute la ligne de prise de vue. L'étiquette « ISO » était grise et
   ses valeurs blanches : deux teintes pour une même information, l'œil s'arrêtait sur la
   rupture au lieu de lire la ligne. Un gris clair unique, en retrait du titre sans être terne. */
.exif { font-family:var(--mono); display:inline-flex; flex-wrap:wrap; gap:0.7rem; color:rgba(255,255,255,0.72); font-size:clamp(0.75rem,1.1vw,0.76rem); letter-spacing:0.08em; text-transform:uppercase; }
.exif .dim { color:inherit; }

/* ---------- Acquérir (bas-centre) ---------- */
/* « Acquérir » se cale à DROITE de la photo, pas au centre de la fenêtre. Centré, il tombait sur
   le cartel — qui occupe le bas gauche — dès que la photo se rétrécissait, c'est-à-dire dès que le
   panneau des séries s'ouvrait. L'information à gauche, l'action à droite : ils ne se croisent
   plus, et l'œil sait où chercher quoi. */
.acquire { position:absolute; left:auto;
  /* Aligné sur le bord droit de la PHOTO, mais jamais assez à droite pour toucher la grappe de
     contrôles : sur un écran large, la photo va plus loin que la molette. `max()` prend celui des
     deux qui écarte le plus. */
  right:max(calc(100% - var(--frame-l, 0px) - var(--frame-w, 100%) + var(--hud-in-x)), calc(var(--hud-pad) + 9rem));
  bottom:calc(100% - var(--frame-t, 0px) - var(--frame-h, 100%) + var(--hud-in-y) + var(--dock-lift));
  transform:none; z-index:3;
  display:inline-flex; align-items:center; gap:0.5rem; cursor:pointer;
  font-family:var(--mono); font-size:0.8rem; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--blanc); background:rgba(10,10,9,0.5); border:1px solid rgba(232,199,154,0.55);
  padding:0.7rem 1.3rem; border-radius:2px; backdrop-filter:blur(6px);
  transition:border-color 0.4s var(--ease),background 0.4s var(--ease),opacity 0.7s var(--ease); }
.acquire:hover { border-color:var(--ambre); background:rgba(232,199,154,0.14); }
.acquire__plus { color:var(--ambre); font-size:1.05rem; line-height:0; }
.viewfinder__hit { position:absolute; inset:0; background:transparent; border:0; cursor:grab; z-index:1; touch-action:none; }
.viewfinder.panning .viewfinder__hit { cursor:grabbing; }
.viewfinder.fitmode .viewfinder__hit { cursor:pointer; }
/* Curseur objectif : au repos (~1 s), la flèche devient un objectif photo (voir viseur.js) */
body.lenscursor, body.lenscursor .viewfinder__hit, body.lenscursor .ctl, body.lenscursor .acquire, body.lenscursor .dial, body.lenscursor .fitbtn {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='34' viewBox='0 0 34 34'%3E%3Ccircle cx='17' cy='17' r='14' fill='rgba(5,5,5,0.5)' stroke='%23e8c79a' stroke-width='1.6'/%3E%3Ccircle cx='17' cy='17' r='9' fill='none' stroke='%23e8c79a' stroke-width='1.1' opacity='0.85'/%3E%3Ccircle cx='17' cy='17' r='3' fill='%23e8c79a'/%3E%3Cg stroke='%23e8c79a' stroke-width='1.3'%3E%3Cline x1='17' y1='1.5' x2='17' y2='6'/%3E%3Cline x1='17' y1='28' x2='17' y2='32.5'/%3E%3Cline x1='1.5' y1='17' x2='6' y2='17'/%3E%3Cline x1='28' y1='17' x2='32.5' y2='17'/%3E%3C/g%3E%3C/svg%3E") 17 17, auto;
}
/* Vue « image entière » : on voit toute la photo, bandes noires (letterbox) */
.viewfinder.fitmode .stage__img { object-fit:contain; }
.viewfinder.fitmode .stage__vignette { opacity:0.25; }

/* ---------- Panier (déclencheur) ---------- */
.ctl { position:absolute; z-index:4; font-family:var(--mono); font-size:0.8rem; letter-spacing:0.12em;
  text-transform:uppercase; color:var(--gris-2); background:transparent; border:0; cursor:pointer;
  padding:0.3rem; display:inline-flex; align-items:center; gap:0.4rem; opacity:0.85; transition:color 0.3s var(--ease),opacity 0.7s var(--ease); }
.ctl:hover,.ctl:focus-visible { color:var(--blanc); opacity:1; outline:none; }
/* ---------- PANIER, dans la grappe de contrôles du bas ----------
   Même gabarit que le bouton de cadrage, posé au-dessus de lui : molette, cadrage, panier.
   Une seule zone de commandes sur tout le site, atteignable au pouce et déjà sous la souris.
   Au repos il s'efface presque : sur un site dont l'image est le produit, un panier vide n'a
   rien à réclamer. Il s'allume et compte dès qu'il contient quelque chose. */
.ctl--cart { position:fixed; z-index:24; top:auto;
  /* Aligné sur le bouton de cadrage : même axe vertical, et 58 px au-dessus de lui
     (46 de hauteur + 12 d'écart), soit le même intervalle qu'entre le cadrage et la molette. */
  right:calc(var(--hud-pad) + 25px + env(safe-area-inset-right));
  bottom:calc(var(--hud-pad) + 166px + env(safe-area-inset-bottom));
  width:46px; height:46px; border-radius:50%; display:grid; place-items:center;
  color:var(--gris-2); background:rgba(8,8,7,0.5); backdrop-filter:blur(8px);
  border:1.5px solid rgba(232,199,154,0.4); box-shadow:0 8px 30px rgba(0,0,0,0.5);
  opacity:0.82; transition:opacity 0.35s var(--ease), color 0.35s var(--ease),
    border-color 0.35s var(--ease), background 0.35s var(--ease); }
.ctl--cart:hover, .ctl--cart:focus-visible { opacity:1; color:var(--noir); background:var(--ambre);
  border-color:var(--ambre); outline:none; }
/* Non vide : il cesse d'être discret. C'est le seul chemin vers l'achat, il n'a pas à être timide. */
.ctl--cart.has-items { opacity:1; color:var(--ambre); border-color:var(--ambre);
  background:rgba(232,199,154,0.14); }
.ctl--cart.has-items:hover, .ctl--cart.has-items:focus-visible { color:var(--noir); background:var(--ambre); }

/* Étiquette au survol, comme le bouton de cadrage. Le libellé change avec l'état : « Panier »
   quand il est vide, « Commander » dès qu'il contient quelque chose — c'est à ce moment-là que
   le mot compte. */
.ctl--cart::after {
  content:attr(data-lbl); position:absolute; right:calc(100% + 0.6rem); top:50%; transform:translateY(-50%);
  font-family:var(--mono); font-size:0.75rem; letter-spacing:0.12em; text-transform:uppercase;
  color:var(--blanc); background:rgba(8,8,7,0.86); backdrop-filter:blur(6px);
  border:1px solid var(--line); border-radius:999px; padding:0.4rem 0.7rem; white-space:nowrap;
  opacity:0; pointer-events:none; transition:opacity 0.25s var(--ease); }
.ctl--cart:hover::after, .ctl--cart:focus-visible::after { opacity:1; }
.ctl--cart.has-items::after { border-color:rgba(232,199,154,0.5); color:var(--ambre); }
@media (hover:none) { .ctl--cart::after { display:none; } }

/* Un battement unique à l'ajout : le panier est en bas à droite, l'ajout se fait au centre.
   Sans ce signal, on ne voit pas où va ce qu'on vient de mettre au panier. */
.ctl--cart.just-added { animation:panieradd 0.7s var(--ease) 2; }
@keyframes panieradd { 0%,100% { transform:scale(1); } 45% { transform:scale(1.14); } }
@media (prefers-reduced-motion: reduce) { .ctl--cart.just-added { animation:none; } }
.ctl--cart .ctl__lbl { display:none; }
.ctl--cart .cartbadge { position:absolute; top:-5px; right:-5px; min-width:19px; height:19px;
  display:none; place-items:center; padding:0 5px; border-radius:999px;
  background:var(--ambre); color:var(--noir); font-family:var(--mono); font-size:0.75rem; line-height:1; }
.ctl--cart.has-items .cartbadge { display:grid; }
/* « Voir la galerie » — révèle la barre du bas (surtout pour le tactile) */
/* le bouton se décale avec la colonne pour ne pas la recouvrir */
/* Le bouton se cale sur la PHOTO comme le bandeau, et passe SOUS lui : posés à la même hauteur,
   « Séries » et « Série · Ralentir » s'écrasaient l'un sur l'autre. */
.ctl--gallery { z-index:7; left:calc(var(--frame-l, var(--hud-pad)) + var(--hud-in-x));
  top:calc(var(--frame-t, var(--hud-pad)) + var(--hud-in-y) + 2.3rem); gap:0.45rem; opacity:1;
  transition:left 0.45s var(--ease), opacity 0.7s var(--ease);
  border:1px solid rgba(232,199,154,0.45); border-radius:999px; padding:0.42rem 0.82rem; }
.ctl--gallery svg { color:var(--ambre); }
/* Cibles tactiles : 44 px minimum (mesure du harnais — 7 commandes étaient en dessous). */
.ctl, .btn-ghost, .btn-primary, .acquire, .story__skip { min-height:44px; }
.ctl { display:inline-flex; align-items:center; }
.ctl--cart { display:grid; }   /* le panier est une pastille ronde, pas une étiquette en ligne */
.story__skip { padding:0.8rem 0.5rem; }

/* Le panier est stylé plus haut, avec la grappe de contrôles du bas. Les règles qui vivaient ici
   dataient de sa position en haut à droite et l'écrasaient, étant plus tardives dans le fichier. */
.ctl--gallery:hover,.ctl--gallery:focus-visible { color:var(--blanc); border-color:var(--ambre); opacity:1; }
body.dock-on .ctl--gallery { color:var(--ambre); border-color:var(--ambre); }   /* actif quand le panneau est ouvert */
/* Le panneau est une SUPERPOSITION fixe : il ne pousse pas le viseur, il le recouvre. Le bouton
   qui l'ouvre doit donc s'écarter quand il est ouvert, sinon il se retrouve posé sur la liste des
   séries. Il reste visible, pour pouvoir refermer. */
body.dock-on .ctl--gallery { left:calc(var(--dock-w, 384px) + var(--hud-in-x)); }
@media (max-width: 680px) {
  /* En plein écran, le panneau recouvre tout : le bouton n'a nulle part où aller, il s'efface.
     On referme par la croix du panneau ou par un appui hors vignette. */
  body.dock-on .ctl--gallery { opacity:0; pointer-events:none; }
}
/* Bouton « image entière » — pastille ronde au-dessus de la molette */
.fitbtn { position:fixed; z-index:24; right:calc(var(--hud-pad) + 25px); bottom:calc(var(--hud-pad) + 108px);
  width:46px; height:46px; border-radius:50%; display:grid; place-items:center; cursor:pointer;
  color:var(--gris-2); background:rgba(8,8,7,0.42); backdrop-filter:blur(8px);
  border:1.5px solid var(--gris-2); box-shadow:0 8px 30px rgba(0,0,0,0.5);
  transition:color 0.3s var(--ease),border-color 0.3s var(--ease),opacity 0.7s var(--ease); }
.fitbtn svg { width:22px; height:22px; }
.fitbtn:hover,.fitbtn:focus-visible { color:var(--blanc); border-color:var(--ambre); outline:none; }
.fitbtn[aria-pressed="true"] { color:var(--ambre); border-color:var(--ambre); }

/* ---------- Molette ---------- */
/* Molette de menu : fixe et TOUJOURS accessible, y compris par-dessus les panneaux ouverts
   (à propos, contact, séries, légal…) — plus besoin de descendre chercher « Retour au menu ».
   Masquée seulement sur les écrans transactionnels (configurateur, panier), qui ont leur « Fermer ». */
.dial { position:fixed; z-index:24; right:var(--hud-pad); bottom:var(--hud-pad);
  width:96px; height:96px; border-radius:50%; border:0; cursor:pointer; background:rgba(8,8,7,0.42);
  backdrop-filter:blur(8px); display:grid; place-items:center; color:var(--blanc); box-shadow:0 8px 30px rgba(0,0,0,0.5); }
body.panel-full .dial { opacity:0; pointer-events:none; }
.dial__ring { position:absolute; inset:0; border-radius:50%; border:1.5px solid var(--gris-2); opacity:0.95; }
.dial__ticks { position:absolute; inset:4px; border-radius:50%; opacity:0.75; transition:transform 0.6s var(--ease);
  background:repeating-conic-gradient(from 0deg,var(--gris-2) 0 1.4deg,transparent 1.4deg 12deg);
  -webkit-mask:radial-gradient(circle,transparent 60%,#000 62%,#000 74%,transparent 76%);
          mask:radial-gradient(circle,transparent 60%,#000 62%,#000 74%,transparent 76%); }
.dial__pointer { position:absolute; top:4px; left:50%; transform:translateX(-50%); width:0; height:0; border-left:5px solid transparent; border-right:5px solid transparent; border-bottom:8px solid var(--ambre); }
.dial__label { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.16em; text-transform:uppercase; color:var(--blanc); }
.dial:hover,.dial:focus-visible { outline:none; transform:scale(1.05); }
.dial:hover .dial__ring,.dial:focus-visible .dial__ring { border-color:var(--ambre); }
.dial:hover .dial__ticks,.dial:focus-visible .dial__ticks { transform:rotate(20deg); }
.dial.call { animation:dialcall 2.4s var(--ease) 3; }
@keyframes dialcall { 0%,100%{box-shadow:0 8px 30px rgba(0,0,0,0.5);} 50%{box-shadow:0 0 0 6px rgba(232,199,154,0.16),0 8px 30px rgba(0,0,0,0.5);} }

/* ---------- Dock bas (séries + pellicule) — flotte sur l'image, s'efface pour l'immersion ---------- */
/* Découplée de la navigation : cachée par défaut, remonte via body.dock-on
   (souris en zone basse sur desktop, tap sur mobile) — jamais à la navigation. */
/* ---------- Galerie LATÉRALE (v10) ----------
   Elle s'ouvre sur le flanc gauche au lieu de pousser le cartel vers le haut : le texte ne
   monte plus et ne descend plus quand on ouvre la pellicule.
   La version à barre horizontale est gardée dans 02_WEB/_archive/prototype-v9-galerie-en-bas/. */
.bottomdock { position:fixed; left:0; top:0; bottom:0; z-index:6; width:var(--dock-w,296px);
  display:flex; flex-direction:row; background:linear-gradient(90deg,#0a0a09 88%,rgba(10,10,9,0.9));
  border-right:1px solid var(--line);
  opacity:0; transform:translateX(-14px); pointer-events:none;
  transition:opacity 0.45s var(--ease), transform 0.45s var(--ease); }
body.dock-on .bottomdock { opacity:1; transform:none; pointer-events:auto; }
.bottomdock::before { content:""; position:absolute; top:0; bottom:0; left:100%; width:44px; pointer-events:none;
  background:linear-gradient(90deg, rgba(0,0,0,0.5), transparent); }

/* ---------- Navigation des séries (en tête de colonne) ---------- */
/* Colonne 1 — les séries, toujours à l'écran. Défile quand il y en aura beaucoup. */
.seriesbar { flex:0 0 134px; display:flex; flex-direction:column; gap:2px; padding:10px 8px;
  background:#0a0a09; border-right:1px solid var(--line); overflow-y:auto; scrollbar-width:thin; }
.serieslist__t { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.16em; text-transform:uppercase;
  color:var(--gris); padding:0 0.35rem 0.5rem; flex:0 0 auto; }
.serieitem { display:flex; flex-direction:column; gap:0.12rem; width:100%; padding:0.6rem 0.5rem;
  background:transparent; border:0; border-left:2px solid transparent; border-radius:2px;
  cursor:pointer; text-align:left; flex:0 0 auto;
  transition:background 0.25s var(--ease), border-color 0.25s var(--ease); }
.serieitem:hover { background:rgba(255,255,255,0.05); }
.serieitem[aria-current="true"] { background:rgba(232,199,154,0.12); border-left-color:var(--ambre); }
/* contraste mesuré à 3,73 : remonté au-dessus du seuil AA (4,5) */
.serieitem__n { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.1em; color:var(--gris-2); }
.serieitem__name { font-family:var(--serif); font-size:1.12rem; color:var(--gris-2); line-height:1.12; }
.serieitem[aria-current="true"] .serieitem__name { color:var(--ambre); }
.serieitem__c { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.06em; text-transform:uppercase; color:var(--gris-2); }

/* Colonne 2 — la pellicule, façon bande de film : perforations sur les bords. */
.filmstrip { position:relative; background:
  repeating-linear-gradient(#141412 0 8px, #0a0a09 8px 16px) left  / 7px 100% repeat-y,
  repeating-linear-gradient(#141412 0 8px, #0a0a09 8px 16px) right / 7px 100% repeat-y,
  var(--noir); }

/* ---------- Pellicule ---------- */
.filmstrip { flex:1 1 auto; display:flex; flex-direction:column; gap:6px; padding:8px 12px; overflow-y:auto; overflow-x:hidden; scrollbar-width:thin; }
.frame { position:relative; flex:0 0 auto; width:100%; aspect-ratio:3/2; border:0; padding:0; cursor:pointer; background:none; opacity:0.5; transition:opacity 0.4s var(--ease); }
.frame img { width:100%; height:100%; object-fit:contain; } /* #113 : c'est le tirage, pas une vignette */
.frame::after { content:""; position:absolute; inset:0; border:1px solid transparent; }
.frame[aria-current="true"] { opacity:1; }
.frame[aria-current="true"]::after { border-color:var(--ambre); }
.frame:hover { opacity:0.85; }
.frame:focus-visible { outline:2px solid var(--ambre); outline-offset:2px; }
/* Légende de vignette : masquée sur grand écran, où la bande latérale est étroite et où le
   cartel affiche déjà le titre de la vue en cours. */
.frame__lgd { display:none; }
@media (max-width: 680px) {
  /* Le bouton devient une colonne : image, puis légende. Sans ça, l'image gardait `height:100%`
     de la règle générale, elle prenait TOUTE la hauteur de la cellule, et la légende débordait
     par le bas — elle allait s'écrire sur la vignette de la rangée suivante.
     Le liseré de sélection passe sur l'IMAGE et non sur le bouton, sinon il encadrait aussi le
     texte, ce qui n'a pas de sens : c'est la vue qui est courante, pas son titre. */
  .frame { aspect-ratio:auto; display:flex; flex-direction:column; align-items:stretch; }
  .frame img { height:auto; aspect-ratio:3/2; }
  .frame::after { display:none; }
  .frame[aria-current="true"] img { outline:1px solid var(--ambre); outline-offset:-1px; }
  .frame__lgd { display:flex; align-items:baseline; gap:0.45rem; padding:0.4rem 0.1rem 0; text-align:left; }
  .frame__no  { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.1em; color:var(--ambre); flex:0 0 auto; }
  .frame__ttl { font-family:var(--serif); font-size:0.86rem; color:var(--blanc); opacity:0.86;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .frame { opacity:0.78; }
  .frame[aria-current="true"] { opacity:1; }
}

/* ---------- Overlays ---------- */
.scrim { position:fixed; inset:0; background:rgba(3,3,3,0.72); backdrop-filter:blur(4px); z-index:20; animation:fade 0.4s var(--ease); }
.ov { position:fixed; z-index:21; color:var(--blanc); animation:fade 0.45s var(--ease); }
@keyframes fade { from{opacity:0;} to{opacity:1;} }
.ov__close { position:absolute; top:clamp(1rem,3vw,2rem); right:clamp(1rem,3vw,2rem); background:transparent; border:0; color:var(--gris-2); font-family:var(--mono); font-size:0.8rem; letter-spacing:0.12em; text-transform:uppercase; cursor:pointer; z-index:2; }
.ov__close:hover { color:var(--blanc); }

/* Menu LCD (agrandi) */
.ov--lcd { right:var(--hud-pad); bottom:calc(var(--hud-pad) + 122px); z-index:22; }
.lcd { position:relative; width:min(540px,93vw); background:radial-gradient(120% 120% at 50% 0%,#0c0f0c,#070907);
  border:9px solid #17170f; border-radius:16px; padding:1.1rem 1.4rem 1.3rem;
  box-shadow:0 24px 70px rgba(0,0,0,0.65), inset 0 0 30px rgba(0,0,0,0.5); overflow:hidden; animation:lcdrise 0.35s var(--ease); }
@keyframes lcdrise { from{opacity:0; transform:translateY(14px) scale(0.98);} to{opacity:1; transform:none;} }
.lcd::after { content:""; position:absolute; inset:0; pointer-events:none; opacity:0.5; background:repeating-linear-gradient(to bottom,rgba(255,255,255,0.025) 0 1px,transparent 1px 3px); }
.lcd__bar { display:flex; justify-content:space-between; align-items:center; padding-bottom:0.6rem; margin-bottom:0.4rem; border-bottom:1px solid rgba(232,199,154,0.18); }
.lcd__title { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.16em; text-transform:uppercase; color:var(--ambre); }
.lcd__nav { display:flex; flex-direction:column; }
.lcd__row { display:flex; align-items:center; gap:0.9rem; padding:0.82rem 0.4rem; border-bottom:1px solid rgba(255,255,255,0.05); transition:background 0.25s var(--ease); }
.lcd__row:hover,.lcd__row:focus-visible { background:rgba(232,199,154,0.1); outline:none; }
/* Fin de bloc : le trait s'affirme et une respiration s'ouvre en dessous. Assez pour qu'on lise
   trois groupes, trop peu pour qu'on croie à trois menus. Pas de titre de bloc : les libellés se
   suffisent, et trois intertitres alourdiraient un menu qui tient déjà en un coup d'œil. */
.lcd__row--fin { border-bottom-color:rgba(232,199,154,0.22); margin-bottom:0.85rem; }
.lcd__no { font-family:var(--mono); font-size:0.78rem; color:var(--ambre); letter-spacing:0.08em; }
.lcd__lbl { font-family:var(--serif); font-size:1.65rem; color:var(--blanc); flex:1 1 auto; }
.lcd__ar { font-family:var(--mono); color:var(--ambre); opacity:0; transform:translateX(-4px); transition:opacity 0.25s var(--ease),transform 0.25s var(--ease); }
.lcd__row:hover .lcd__ar,.lcd__row:focus-visible .lcd__ar { opacity:1; transform:none; }
/* Instagram valorisé */
/* Langue — trois pastilles au pas du sélecteur de mode d'un boîtier. La langue courante
   est en ambre PLEIN : un simple soulignement ne suffit pas, l'écart de contraste entre
   l'état courant et les autres doit rester lisible en plein soleil comme le reste du HUD. */
.lcd__lang { display:flex; gap:0.4rem; margin-top:0.9rem; }
.lang { flex:1 1 0; min-height:44px; padding:0.5rem 0; background:transparent; color:var(--gris-2);
  border:1px solid var(--line); border-radius:4px; font-family:var(--mono); font-size:0.78rem;
  letter-spacing:0.14em; cursor:pointer; transition:color 0.3s var(--ease),border-color 0.3s var(--ease),background 0.3s var(--ease); }
.lang abbr { text-decoration:none; border:0; }      /* pas de pointillé sous DE / EN */
.lang:hover { color:var(--blanc); border-color:var(--gris-2); }
.lang[aria-current="true"] { background:var(--ambre); color:#1a1508; border-color:var(--ambre); cursor:default; }
.lcd__ig { display:flex; align-items:center; gap:0.7rem; margin-top:0.8rem; padding:0.7rem 0.8rem; border:1px solid rgba(232,199,154,0.35); border-radius:4px; color:var(--blanc); transition:background 0.3s var(--ease),border-color 0.3s var(--ease); }
.lcd__ig:hover { background:rgba(232,199,154,0.12); border-color:var(--ambre); }
.lcd__ig svg { color:var(--ambre); flex:0 0 auto; }
.lcd__ig-h { font-family:var(--serif); font-size:1.05rem; flex:1 1 auto; }
.lcd__ig-n { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase; color:var(--ambre); }
.lcd__close { top:0.6rem; right:0.7rem; font-size:1rem; }
.lcd__back { display:inline-flex; align-items:center; gap:0.6rem; background:rgba(232,199,154,0.08); border:1px solid rgba(232,199,154,0.35); border-radius:999px; color:var(--ambre); font-family:var(--mono); font-size:0.82rem; letter-spacing:0.14em; text-transform:uppercase; cursor:pointer; padding:0.62rem 1.3rem; margin-bottom:0.7rem; }
.lcd__back:hover,.lcd__back:focus-visible { background:var(--ambre); color:var(--noir); outline:none; }
/* Le sous-menu Légal a exactement la même typographie et le même gabarit que le menu principal :
   `--lcd-h` est la hauteur du menu principal, mesurée en JS à la première ouverture. */
.lcd__view { min-height:var(--lcd-h,auto); }

/* « Fermer » collant en tête de page : toujours atteignable, même après avoir fait défiler. */
.ovclose { position:sticky; top:0; z-index:5; align-self:flex-end; flex:0 0 auto;
  display:inline-flex; align-items:center; gap:0.5rem; margin:-0.4rem -0.4rem 0.6rem 0;
  font-family:var(--mono); font-size:0.8rem; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--ambre); background:#191510; border:1px solid var(--ambre); border-radius:999px;
  padding:0.5rem 1.05rem; cursor:pointer; transition:background 0.25s var(--ease), color 0.25s var(--ease); }
.ovclose:hover, .ovclose:focus-visible { background:var(--ambre); color:var(--noir); outline:none; }
.ov--menu .ovclose { position:absolute; top:clamp(1rem,3vw,2rem); right:clamp(1rem,3vw,2rem); margin:0; }

/* Barre « Retour » (bas des panneaux) — bien visible */
/* La barre « Retour » est collante : sans cette réserve, elle passait par-dessus la fin du texte. */
.ov--doc .doc { padding-bottom:1.5rem; }

/* Séries (grille) */
/* Séries : plein écran, mais on réserve la colonne de droite pour le menu LCD qui reste ouvert. */
.ov--menu { inset:0; background:linear-gradient(160deg,#0b0b0a,#050505); display:flex; flex-direction:column; overflow-y:auto; padding:clamp(1.5rem,6vw,5rem); }
/* Sur large écran le menu occupe la droite : on décale le contenu pour qu'il ne passe pas dessous.
   Sur mobile le LCD prend toute la largeur — pas de décalage, il se superpose. */
@media (min-width: 1100px) {
  body.menu-open .ov--menu { padding-right:calc(540px + 2 * var(--hud-pad)); }
  body.menu-open .ov--doc,
  body.menu-open .ov--cookies { left:calc(50% - (540px + var(--hud-pad)) / 2); }
}
.ov--menu .series { margin-top:clamp(2.5rem,8vh,5rem); }
.ov__wordmark { position:absolute; top:clamp(1.2rem,3vw,2rem); left:clamp(1.2rem,4vw,3rem); font-family:var(--serif); letter-spacing:0.2em; color:var(--blanc); opacity:0.9; }
/* Sur écran étroit, ce titre posé en absolu retombait SUR le premier paragraphe : il est hors
   du flux, donc rien ne lui réserve de place, et le texte lui passait dessous. Il rentre dans
   le flux et pousse le contenu, ce qu'il aurait dû faire dès le départ à cette largeur. */
@media (max-width: 680px) {
  .ov__wordmark { position:static; display:block; margin:0 0 0.9rem; }
}
.series { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:1rem; max-width:940px; }
/* color explicite : .serie est un <button>, qui n'hérite pas la couleur du body (noir par défaut). */
.serie { position:relative; border:0; padding:0; cursor:pointer; background:none; color:var(--blanc); text-align:left; overflow:hidden; border-radius:3px; display:block; }
.serie__img { position:relative; aspect-ratio:4/3; overflow:hidden; }
.serie__img img { width:100%; height:100%; object-fit:cover; transition:transform 0.5s var(--ease),filter 0.4s var(--ease); filter:brightness(0.72); }
.serie:hover .serie__img img { transform:scale(1.04); filter:brightness(0.9); }
.serie__cap { position:absolute; left:0; right:0; bottom:0; padding:1rem; background:linear-gradient(transparent,rgba(0,0,0,0.8)); }
.serie__name { font-family:var(--serif); font-size:1.6rem; color:var(--blanc); text-shadow:0 2px 18px rgba(0,0,0,0.75); }
.serie__n { font-family:var(--mono); font-size:0.76rem; letter-spacing:0.1em; text-transform:uppercase; color:var(--ambre); }
.serie__desc { color:var(--gris-2); font-size:0.98rem; line-height:1.45; margin-top:0.5rem; }
.serie__rule { color:var(--gris); font-size:0.8rem; line-height:1.4; margin-top:0.55rem;
  padding-top:0.5rem; border-top:1px solid rgba(255,255,255,0.07); }
.serie[aria-current="true"] .serie__img { outline:2px solid var(--ambre); outline-offset:-2px; }

/* Séries en rotation : édition close (plus en vente) vs en vente */
.badge { display:inline-block; font-family:var(--mono); font-size:0.75rem; letter-spacing:0.12em; text-transform:uppercase;
  padding:0.24rem 0.5rem; border-radius:999px; border:1px solid currentColor; line-height:1; }
.badge--closed { color:var(--gris-2); background:rgba(8,8,7,0.72); }
.badge--onsale { color:var(--vert); background:rgba(8,8,7,0.55); }
.serie__badge { position:absolute; top:0.7rem; left:0.7rem; z-index:2; backdrop-filter:blur(4px); }
/* Pastille d'état dans le cartel : elle se lit avec le numéro et le lieu, donc AVANT le bouton. */
.cartel__statut { margin-left:0.6rem; vertical-align:0.06em; }
.serie--closed .serie__img img { filter:brightness(0.5) grayscale(0.35); }
.serie--closed:hover .serie__img img { filter:brightness(0.62) grayscale(0.2); }
.series__policy { max-width:940px; margin:0 0 1.4rem; color:var(--gris-2); font-size:1rem; line-height:1.55; }
.series__policy strong { color:var(--blanc); font-weight:600; }
.series__policy .badge { vertical-align:middle; }
/* Une édition close n'est pas une vente empêchée, c'est un autre état. Le bouton ne doit donc
   pas se comporter comme un appel à l'achat : il redevenait AMBRE au survol, exactement comme
   « Acquérir », et il redevenait donc une invitation à acheter ce qui n'est plus à vendre. */
.acquire--closed { border-color:var(--gris); color:var(--gris-2); background:transparent; }
.acquire--closed .acquire__plus { color:var(--gris-2); }
.acquire--closed:hover, .acquire--closed:focus-visible {
  border-color:var(--gris-2); color:var(--blanc); background:rgba(255,255,255,0.05); }
/* opt-in newsletter (configurateur) */
.optin { display:flex; align-items:flex-start; gap:0.5rem; margin:0.7rem 0 0; font-size:0.94rem; line-height:1.4; color:var(--gris-2); cursor:pointer; }
.optin input { margin-top:0.15rem; accent-color:var(--ambre); flex:0 0 auto; }
.optin strong { color:var(--blanc); font-weight:600; }

/* Documents */
/* Les panneaux de contenu s'ouvrent AU CENTRE de l'écran (et non plus en tiroir à droite),
   pour laisser la molette et le menu LCD commander depuis la droite. */
.ov--doc { top:50%; left:50%; transform:translate(-50%,-50%);
  width:min(680px,92vw); max-height:min(86vh,86dvh);
  background:linear-gradient(180deg,#0c0c0b,#080807); border:1px solid var(--line); border-radius:5px;
  padding:clamp(1.8rem,4vw,3rem); overflow-y:auto; box-shadow:0 30px 80px rgba(0,0,0,0.66);
  display:flex; flex-direction:column; }
.ov--doc .doc { max-width:none; }
.doc { max-width:46ch; }
.doc__kicker { font-family:var(--mono); font-size:0.8rem; letter-spacing:0.18em; text-transform:uppercase; color:var(--ambre); margin-bottom:0.8rem; }
.doc__title { font-family:var(--serif); font-weight:400; font-size:clamp(2rem,5vw,3rem); line-height:1.05; margin-bottom:1rem; }
.doc__lede { color:var(--gris-2); font-size:1.05rem; margin-bottom:1.5rem; }
/* Reprise du diaporama, depuis la fiche de Kim. Haut du panneau, pour être vu sans dérouler :
   ce bouton remplace une entrée de menu supprimée, s'il se cache il n'existe pas. */
.doc__replay { margin:0 0 1.8rem; }

/* ---------- Médias ----------
   Discret par construction : un intertitre en petites capitales, une liste sans puce, un lien qui
   ne se distingue qu'au survol. Tant qu'il n'y a qu'une parution, une rubrique en ferait trop. */
.medias { margin:1.8rem 0 0; padding-top:1.2rem; border-top:1px solid var(--line); }
.medias__label { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.16em;
  text-transform:uppercase; color:var(--gris); margin-bottom:0.5rem; }
.medias__list { list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:0.3rem; }
.medias__link { display:inline-flex; align-items:baseline; gap:0.4rem; min-height:44px;
  align-items:center; font-family:var(--sans); font-size:1.05rem; color:var(--gris-2);
  text-decoration:none; transition:color 0.25s var(--ease); }
.medias__link:hover, .medias__link:focus-visible { color:var(--blanc); outline:none; }
.medias__link:focus-visible { outline:2px solid var(--ambre); outline-offset:3px; }
.medias__t { color:var(--gris); }
.medias__link:hover .medias__t { color:var(--gris-2); }
.medias__ar { font-size:0.8rem; color:var(--ambre); }

/* ---------- Paiement différé ----------
   Posé AVANT les moyens de paiement, pas après : l'information qui peut changer une décision
   d'achat doit arriver avant que la décision soit prise. */
.pay__klarna { font-size:1rem; line-height:1.5; color:var(--gris-2);
  background:rgba(232,199,154,0.07); border:1px solid rgba(232,199,154,0.22);
  border-radius:3px; padding:0.7rem 0.9rem; margin-bottom:1rem; }
.pay__klarna strong { color:var(--blanc); font-weight:500; }

/* ---------- Moyens de paiement, au récapitulatif ---------- */
.paymeans { margin-top:1rem; padding-top:0.9rem; border-top:1px solid var(--line); }
.paymeans__t { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.16em;
  text-transform:uppercase; color:var(--gris); margin-bottom:0.45rem; }
.paymeans__l { list-style:none; padding:0; margin:0; display:flex; flex-wrap:wrap; gap:0.35rem; }
.paymeans__l li { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.06em;
  color:var(--gris-2); border:1px solid var(--line); border-radius:2px; padding:0.25rem 0.45rem; }
.paymeans__k { color:var(--ambre) !important; border-color:rgba(232,199,154,0.35) !important; }
.paymeans__n { font-size:0.8rem; line-height:1.45; color:var(--gris); margin-top:0.5rem; }

/* ---------- Rassurance au panier ---------- */
.cartpage__ras { list-style:none; padding:0; margin:0.9rem 0 0;
  display:flex; flex-direction:column; gap:0.3rem; }
.cartpage__ras li { font-size:0.92rem; line-height:1.45; color:var(--gris-2); padding-left:0.85rem; position:relative; }
.cartpage__ras li::before { content:"·"; position:absolute; left:0; color:var(--ambre); }
.cartpage__ras strong { color:var(--blanc); font-weight:500; }

.doc p { color:#d8d6d1; line-height:1.65; margin-bottom:1rem; }
.doc__sign { font-family:var(--serif); font-style:italic; color:var(--blanc); }
.doc__note { color:var(--gris); font-size:1rem; }
.doc__link { color:var(--ambre); border-bottom:1px solid rgba(232,199,154,0.4); }
/* Co-fondateur YOUniFEEL (entité distincte) */
.younifeel { margin-top:2rem; padding-top:1.4rem; border-top:1px solid var(--line); }
.younifeel__label { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase; color:var(--gris); margin:0 0 0.6rem; }
.younifeel__link { display:inline-flex; align-items:center; gap:0.9rem; text-decoration:none; color:var(--blanc); transition:color 0.3s var(--ease); }
.younifeel__link:hover,.younifeel__link:focus-visible { color:var(--ambre); outline:none; }
.younifeel__mark { height:1.5rem; width:auto; fill:currentColor; }
.younifeel__go { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.1em; text-transform:uppercase; color:var(--ambre); }
/* Index légal */
/* Lecteur légal */
#legalBody h3 { font-family:var(--serif); font-weight:400; font-size:1.15rem; color:var(--blanc); margin:1.4rem 0 0.5rem; }
#legalBody p { color:#cfcdc8; line-height:1.6; margin-bottom:0.8rem; font-size:1.05rem; }
#legalBody .maj { font-family:var(--mono); font-size:0.8rem; letter-spacing:0.1em; text-transform:uppercase; color:var(--gris); }

/* Formulaire */
.form { display:flex; flex-direction:column; gap:1rem; margin-top:1.2rem; }
.field { display:flex; flex-direction:column; gap:0.35rem; }
.field span { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.12em; text-transform:uppercase; color:var(--gris-2); }
/* 1rem et pas 0.95rem, soit 16 px et non 15.2 : Safari sur iPhone ZOOME automatiquement sur tout
   champ dont la police descend sous 16 px. La page grossit au premier appui, la mise en page saute,
   et le cadrage ne revient pas tant que le visiteur ne dézoome pas à la main. Sur un tunnel de
   commande, ça se paie en abandons. Ne pas redescendre cette valeur. */
.field input,.field textarea { background:var(--surface); border:1px solid var(--bord-champ); color:var(--blanc); font-family:var(--sans); font-size:1rem; padding:0.7rem 0.8rem; border-radius:2px; resize:vertical; }
.field input:focus,.field textarea:focus { outline:none; border-color:var(--ambre); }
.form__ok { color:var(--vert); font-size:1.05rem; }
.form__err { color:var(--rouge, #c0392b); font-size:1.05rem; }
/* Piège à robots : hors du cadre, pas `display:none` — un automate saute souvent ce qui est
   masqué, et le piège ne se refermerait jamais. */
.hp { position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden; }

/* Boutons */
.btn-primary { width:100%; padding:0.9rem; background:var(--ambre); color:#1a1508; border:0; border-radius:2px; font-family:var(--mono); text-transform:uppercase; letter-spacing:0.12em; font-size:0.8rem; cursor:pointer; transition:filter 0.3s var(--ease); }
.btn-primary:hover { filter:brightness(1.08); }
.btn-primary:disabled { background:var(--surface); color:var(--gris); cursor:not-allowed; }
.btn-primary--link { display:inline-block; width:auto; text-align:center; text-decoration:none; }
.btn-ghost { padding:0.9rem 1.4rem; background:transparent; color:var(--blanc); border:1px solid var(--line); border-radius:2px; font-family:var(--mono); text-transform:uppercase; letter-spacing:0.12em; font-size:0.8rem; cursor:pointer; }
.btn-ghost:hover { border-color:var(--gris-2); }

/* Configurateur */
.ov--acquire { inset:0; display:grid; place-items:center; padding:clamp(1rem,4vw,3rem);
  padding-top:calc(clamp(1rem,4vw,3rem) + env(safe-area-inset-top));
  padding-bottom:calc(clamp(1rem,4vw,3rem) + env(safe-area-inset-bottom)); }
.config { width:min(780px,100%); max-height:90vh; max-height:90dvh; overflow-y:auto; background:linear-gradient(180deg,#111110,#0a0a09); border:1px solid var(--line); border-radius:4px; display:grid; grid-template-columns:0.85fr 1fr; }
/* fermeture bien visible (modals plein écran) */
.ov--acquire .ov__close, .ov--cart .ov__close { top:calc(var(--hud-pad) + env(safe-area-inset-top)); right:var(--hud-pad); z-index:6; }
.ov--acquire .ov__close, .ov--cart .ov__close, .ov--cookies .ov__close {
  background:rgba(8,8,7,0.8); backdrop-filter:blur(8px); border:1px solid var(--gris-2);
  border-radius:999px; color:var(--blanc); padding:0.55rem 1rem; font-size:0.78rem; }
.ov--acquire .ov__close:hover, .ov--cart .ov__close:hover, .ov--cookies .ov__close:hover {
  background:var(--ambre); color:var(--noir); border-color:var(--ambre); }
/* Aperçu du tirage : cadre 2D + inclinaison 3D + mise sur un mur à l'échelle */
.config__preview { position:relative; display:flex; flex-direction:column; background:#0b0b0a; }
.preview__scene { position:relative; flex:1 1 auto; min-height:300px; display:grid; place-items:center; padding:2rem;
  background:radial-gradient(120% 100% at 50% 32%, #1b1b19, #0a0a09); overflow:hidden; perspective:1000px; perspective-origin:50% 42%; transition:background 0.6s var(--ease); }
/* box-sizing:content-box — la largeur imposée au cadre est celle du TIRAGE, et la baguette
   s'ajoute AUTOUR. Avec border-box (la règle générale du site), le rembourrage mangeait la photo
   de l'intérieur : choisir « caisse américaine » ou « tirage encadré » RAPETISSAIT l'image au lieu
   de l'entourer. Or on n'achète pas un tirage plus petit parce qu'on le fait encadrer : c'est
   l'encombrement au mur qui grandit. La simulation disait donc l'inverse de la réalité. */
.frameview { box-sizing:content-box; position:relative; width:max-content; max-width:100%; background:#0a0a09; transform-style:preserve-3d;
  box-shadow:0 26px 55px rgba(0,0,0,0.6), 0 6px 16px rgba(0,0,0,0.45);
  transition:transform 0.7s var(--ease), width 0.6s var(--ease), padding 0.4s var(--ease), background 0.4s var(--ease); }
/* paysage : le cadre est large et bas — on l'élargit pour garder une surface visible comparable */
/* Le cadrage paysage n'impose plus de largeur : elle vient du format choisi, comme le portrait. */
.frameview__mat { transition:padding 0.4s var(--ease), background 0.4s var(--ease); }
/* le ratio réel est posé en JS depuis le format choisi (applyPreview) — 2:3 n'est que le repli */
/* 3:2 partout depuis le ré-export du 26.07 — l'aperçu ne recadre plus rien. */
/* La largeur est posée en ligne par le script : c'est la dimension du TIRAGE. */
/* 🔴 L'APERÇU SUR MUR METTAIT LA PHOTOGRAPHIE EN BOÎTE — corrigé le 24.08.2026.
   Signalé par Yan, capture à l'appui : bandes noires en haut et en bas du tirage accroché.

   Deux causes dans une seule ligne. `aspect-ratio:2/3` **imposait un cadre portrait**, quel que
   soit le format choisi et quelle que soit l'orientation de l'œuvre ; et `object-fit:contain`
   logeait l'image dedans en la ceinturant de noir.

   ⚠️ Or `applyPreview()` calcule déjà le rapport du format retenu et le pose sur le CADRE. La
   règle figée ici l'écrasait : le JavaScript décidait, la CSS refusait. Deux sources pour une
   même mesure, et c'est celle qui ne sait rien du choix du client qui gagnait.

   L'image hérite donc du cadre, et le remplit — **c'est ce que fait un vrai tirage** : on choisit
   un format, le laboratoire recadre. Un tirage encadré ne porte pas de bandes noires. */
.frameview__img { display:block; aspect-ratio:inherit; object-fit:cover; width:100%; height:100%; } /* #113 */
.frameview__edge { position:absolute; top:0; bottom:0; right:0; width:15px; background:linear-gradient(#0c0c0b,#000);
  transform-origin:right center; transform:rotateY(90deg); opacity:0; transition:opacity 0.4s var(--ease); }
/* supports */
.frameview.f-papier { padding:0; box-shadow:0 20px 44px rgba(0,0,0,0.55); }
.frameview.f-caisse { padding:13px; background:linear-gradient(135deg,#232320,#050505); }
.frameview.f-encadre { padding:13px; background:linear-gradient(135deg,#2a2a27,#0d0d0c); }
/* Alu-dibond : la photo est contrecollée sur une plaque d'aluminium, suspendue par un châssis
   caché qui la décolle du mur de deux centimètres. Il n'y a AUCUN cadre, aucune bordure : le
   liseré clair dessiné jusqu'ici en inventait un. Ce qui se voit en vrai, c'est l'ombre portée
   du décollement, et une tranche métallique sur le côté. */
.frameview.f-dibond { padding:0; background:transparent;
  box-shadow:0 3px 0 -1px rgba(0,0,0,0.30), 0 16px 26px rgba(0,0,0,0.34); }
.frameview.f-dibond .frameview__mat { box-shadow:1px 0 0 rgba(255,255,255,0.22) inset,
  -1px 0 0 rgba(255,255,255,0.14) inset; }
/* Le passe-partout d'un encadrement s'ajoute lui aussi autour de la photo, il ne la recouvre pas. */
.frameview__mat { box-sizing:content-box; }
.frameview.f-encadre .frameview__mat { padding:15px; background:#f1eee8; }
/* inclinaison 3D */
.frameview__edge { opacity:0; }   /* plus d'inclinaison : la tranche ne se voit jamais */
/* ---------- Mise en situation : élévation plate (2D) ----------
   Choix assumé : pas de perspective, pas de rotation. Un mur, un sol, un meuble —
   le tirage se juge sur sa taille, pas sur un effet.
   Échelle : la largeur de la scène vaut WALL_CM (voir viseur.js) ; le tirage est dimensionné en %. */
/* ---------- La scène murale est définie en CENTIMÈTRES, pas en proportions de fenêtre ----------
   Elle représente un pan de mur de 205 cm de large sur 230 cm de haut. Sans rapport d'aspect fixe,
   la hauteur suivait celle de la fenêtre alors que le tirage était dimensionné sur la LARGEUR :
   un 50 × 75 tenait sur un écran de bureau et débordait de 57 px au-dessus du mur sur un iPhone.
   Le repère d'échelle mentait donc selon l'appareil, ce qui est le seul défaut inacceptable ici.

   Repères verticaux, en pourcentage de la scène :
     ligne mur / sol      24 %   (55 cm)
     haut de la commode   56 %   (55 + 74 cm)
     bas du tirage        64 %   (dégagement d'environ 18 cm)
     un 50 × 75 monte à   96,6 % — le plus grand format tient, avec de la marge. */
.preview__scene.is-wall { padding:0; overflow:hidden; min-height:0;
  aspect-ratio:205/230; height:auto; width:100%; margin:0 auto;
  background:linear-gradient(#efeae2 0%, #e4ded4 76%, #b9b0a2 76%, #a79e90 100%); }
.mur { display:none; }
.preview__scene.is-wall .mur { display:block; position:absolute; inset:0; }
/* plinthe le long de la jonction mur / sol */
.preview__scene.is-wall .mur__plinthe { position:absolute; left:0; right:0; bottom:24%; height:1.8%;
  background:#cdc5b8; }

/* Commode adossée au mur : sa base repose sur la ligne mur/sol. */
/* Commode : 110 cm sur un mur de 205 → 54 %. Hauteur exprimée en % de la SCÈNE, et non par un
   rapport d'aspect : le tirage doit pouvoir s'ancrer au-dessus d'elle, et un rapport d'aspect
   dépend de la largeur, donc la hauteur du meuble variait avec la fenêtre. */
.preview__scene.is-wall .commode { position:absolute; left:50%; bottom:24%; transform:translateX(-50%);
  width:54%; height:32%; background:linear-gradient(#8d8172 0 62%,#7b7062 62%);
  border-radius:2px 2px 0 0; box-shadow:0 -1px 0 rgba(255,255,255,0.12) inset; }
.preview__scene.is-wall .commode::before { content:""; position:absolute; left:-3%; right:-3%; top:-5%;
  height:7%; background:#9b8f7f; border-radius:2px; }
.preview__scene.is-wall .commode::after { content:""; position:absolute; left:-7%; right:-7%; bottom:-7%;
  height:8%; background:rgba(0,0,0,0.16); filter:blur(4px); border-radius:50%; }
.preview__scene.is-wall .commode i { position:absolute; left:9%; right:9%; height:24%;
  background:rgba(0,0,0,0.14); border-radius:2px; box-shadow:0 1px 0 rgba(255,255,255,0.08); }
.preview__scene.is-wall .commode i:first-child { top:16%; }
.preview__scene.is-wall .commode i:last-child  { top:52%; }

/* le tirage, accroché à plat sur le mur */
/* Le tirage s'ancre par le BAS, juste au-dessus de la commode. Ancré par le haut, il grandissait
   vers le bas et venait chevaucher le meuble dès les grands formats — un tableau posé sur une
   commode, ce qu'on n'accroche jamais. Ligne mur/sol à 38 %, commode 25 %, plus 5 % de dégagement. */
.preview__scene.is-wall .frameview { position:absolute; left:50%; bottom:64%; top:auto;
  transform:translateX(-50%); box-shadow:0 10px 22px rgba(0,0,0,0.3); }
/* à petite échelle, on allège cadre et passe-partout pour que la photo reste lisible */
.preview__scene.is-wall .frameview.f-dibond { padding:0; }
.preview__scene.is-wall .frameview.f-encadre { padding:3px; }
.preview__scene.is-wall .frameview.f-caisse { padding:5px; }
.preview__scene.is-wall .frameview.f-encadre .frameview__mat { padding:5px; }
.preview__scene.is-wall .frameview__mat { overflow:hidden; }
.preview__scene.is-wall .frameview__img { height:auto; display:block; }

/* légende + contrôles */
.btn-primary--devis { background:transparent; color:var(--ambre); border:1.5px solid var(--ambre); }
.preview__cap { padding:0.7rem 1rem 0.2rem; font-family:var(--mono); font-size:0.8rem; letter-spacing:0.05em; color:var(--gris-2); text-align:center; }
.preview__dim { color:var(--ambre); }
.preview__ctrls { display:flex; gap:0.5rem; padding:0.5rem 1rem 1rem; justify-content:center; flex-wrap:wrap; }
.preview__btn { font-family:var(--mono); font-size:0.76rem; letter-spacing:0.1em; text-transform:uppercase; color:var(--gris-2);
  background:transparent; border:1px solid var(--line); border-radius:999px; padding:0.5rem 0.9rem; cursor:pointer;
  transition:color 0.3s var(--ease), background 0.3s var(--ease), border-color 0.3s var(--ease); }
.preview__btn:hover,.preview__btn:focus-visible { color:var(--blanc); border-color:var(--gris); outline:none; }
.preview__btn[aria-pressed="true"] { color:var(--noir); background:var(--ambre); border-color:var(--ambre); }
.config__form { padding:clamp(1.4rem,3vw,2.2rem); display:flex; flex-direction:column; gap:1.4rem; }
.opt { border:0; }
.opt legend { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.16em; text-transform:uppercase; color:var(--gris-2); margin-bottom:0.7rem; }
.opt__row { display:flex; flex-wrap:wrap; gap:0.5rem; }
.chip { font-family:var(--mono); font-size:0.8rem; letter-spacing:0.04em; color:var(--gris-2); background:transparent; border:1px solid var(--line); border-radius:2px; padding:0.5rem 0.7rem; cursor:pointer; transition:border-color 0.3s var(--ease),color 0.3s var(--ease),background 0.3s var(--ease); }
.chip:hover { color:var(--blanc); border-color:var(--gris); }
.chip[aria-pressed="true"] { color:var(--noir); background:var(--ambre); border-color:var(--ambre); }
.chip small { opacity:0.7; }
.config__foot { position:sticky; bottom:0; margin-top:auto; padding-top:1rem; padding-bottom:0.2rem;
  background:linear-gradient(rgba(11,11,10,0),#0b0b0a 28%); }
.config__price { font-family:var(--mono); text-transform:uppercase; letter-spacing:0.1em; display:flex; justify-content:space-between; margin-bottom:1rem; font-size:1rem; color:var(--blanc); }

/* Panier */
.ov--cart { inset:0; background:linear-gradient(160deg,#0b0b0a,#050505); overflow-y:auto; padding:clamp(1.5rem,5vw,4rem); }
.cartpage { max-width:760px; margin:0 auto; }
.cartpage__title { font-family:var(--serif); font-weight:400; font-size:clamp(2.2rem,6vw,3.6rem); margin-bottom:2rem; }
.cartpage__list { list-style:none; }
.cartpage__item { display:flex; gap:1.2rem; align-items:center; padding:1.1rem 0; border-top:1px solid var(--line); }
.cartpage__item img { width:96px; height:68px; object-fit:cover; flex:0 0 auto; }
.cartpage__meta { flex:1 1 auto; }
.cartpage__meta .t { font-family:var(--serif); font-size:1.3rem; }
.cartpage__meta .o { font-family:var(--mono); font-size:0.8rem; letter-spacing:0.06em; color:var(--gris-2); text-transform:uppercase; margin-top:0.3rem; }
.cartpage__price { font-family:var(--mono); color:var(--blanc); white-space:nowrap; }
.cartpage__rm { background:0; border:0; color:var(--gris); cursor:pointer; font-family:var(--mono);
  font-size:1.1rem; width:44px; height:44px; flex:0 0 auto; display:grid; place-items:center; }
/* 44 px : retirer un tirage du panier était une cible de 22 px, la plus petite du parcours d'achat. */
.cartpage__rm:hover { color:var(--rouge); }
.cartpage__empty { color:var(--gris); font-size:1rem; }
.cartpage__foot { margin-top:2rem; border-top:1px solid var(--line); padding-top:1.5rem; max-width:340px; margin-left:auto; }
.cartpage__total { display:flex; justify-content:space-between; font-family:var(--mono); text-transform:uppercase; letter-spacing:0.1em; color:var(--blanc); margin-bottom:1rem; font-size:1.05rem; }

/* Cookies */
.cookiebar { position:fixed; left:0; right:0; bottom:0; z-index:30; display:flex; flex-wrap:wrap; gap:0.5rem 1.4rem; align-items:center; justify-content:space-between;
  padding:0.6rem clamp(1rem,4vw,2.4rem); background:rgba(9,9,8,0.9); backdrop-filter:blur(10px); border-top:1px solid var(--line); animation:slideup 0.45s var(--ease); }
@keyframes slideup { from{transform:translateY(100%);} to{transform:translateY(0);} }
.cookiebar__txt { font-size:0.92rem; color:var(--gris-2); line-height:1.4; }
.cookiebar__txt strong { font-family:var(--sans); font-weight:600; font-size:0.94rem; color:var(--blanc); margin-right:0.5rem; }
.cookiebar__txt p { display:inline; }
.cookiebar__txt a { color:var(--ambre); border-bottom:1px solid rgba(232,199,154,0.4); cursor:pointer; }
.cookiebar__act { display:flex; flex-wrap:wrap; gap:0.6rem; flex:0 0 auto; }
.cookiebar__act .btn-primary,.cookiebar__act .btn-ghost { width:auto; padding:0.5rem 1.1rem; }
/* Refuser et Accepter : STRICTEMENT le même dessin. Même bordure, même couleur, même
   surface. Rendre « Accepter » plus attirant que « Refuser » vicie le consentement
   (EDPB 03/2022, procédés trompeurs) — c'est le défaut le plus fréquent des bandeaux et
   celui qui se voit le plus vite. Ne pas remplir l'un des deux en ambre. */
.btn-equal { background:transparent; color:var(--ambre); border:1.5px solid var(--ambre); }
.btn-equal:hover { filter:none; background:rgba(232,199,154,0.1); }
@media (max-width:640px) {
  .cookiebar { gap:0.7rem; }
  .cookiebar__act { width:100%; }
  .cookiebar__act .btn-equal { flex:1 1 0; }        /* les deux exactement aussi larges */
  .cookiebar__act .btn-ghost { flex:1 0 100%; order:3; }  /* Personnaliser passe dessous */
}

/* Les cookies sont un document légal comme les autres : même largeur, même position.
   Seule la hauteur suit le contenu. */
.ov--cookies { top:50%; left:50%; transform:translate(-50%,-50%); width:min(680px,92vw); max-height:min(86vh,86dvh); overflow-y:auto; }
.cookies { position:relative; width:100%; background:linear-gradient(180deg,#0c0c0b,#080807); border:1px solid var(--line); border-radius:5px; padding:clamp(1.8rem,4vw,3rem); }
.cookies .doc__title { font-size:clamp(1.6rem,4vw,2.2rem); }
.ck { border:1px solid var(--line); border-radius:4px; padding:1rem 1.2rem; margin-top:1rem; }
.ck__head { display:flex; align-items:center; justify-content:space-between; gap:1rem; }
.ck__name { font-weight:600; }
.ck__desc { color:var(--gris-2); font-size:1rem; margin-top:0.4rem; }
.cookies__foot { display:flex; gap:0.7rem; justify-content:flex-end; margin-top:1.5rem; }
.cookies__foot .btn-primary,.cookies__foot .btn-ghost { width:auto; }
.switch { position:relative; display:inline-block; width:46px; height:24px; flex:0 0 auto; }
.switch input { opacity:0; width:0; height:0; }
.slider { position:absolute; inset:0; background:var(--surface); border:1px solid var(--bord-champ); border-radius:24px; cursor:pointer; transition:background 0.3s var(--ease); }
.slider::before { content:""; position:absolute; left:2px; top:2px; width:18px; height:18px; background:var(--gris-2); border-radius:50%; transition:transform 0.3s var(--ease),background 0.3s var(--ease); }
.switch input:checked + .slider { background:var(--ambre); border-color:var(--ambre); }
.switch input:checked + .slider::before { transform:translateX(22px); background:#1a1508; }
.switch input:disabled + .slider { opacity:0.6; cursor:not-allowed; }

/* ---------- Mouvement réduit ---------- */
@media (prefers-reduced-motion: reduce) {
  .stage__img,.frame,.dial,.dial__ticks,.serie__img img { transition:none; }
  .viewfinder.is-focusing .stage__img { filter:saturate(1) brightness(1); }
  .reticle[data-state="focus"] .reticle__mark,.scrim,.ov,.dial.call,.cookiebar { animation:none; }
  body.idle .hud,body.idle .cartel,body.idle .acquire,body.idle .ctl,body.idle .dial { opacity:1; }  /* pas d'auto-effacement */
}

/* ---------- Mobile ---------- */
/* Tablette et fenêtres étroites : « Acquérir », centré, venait se poser sur le cartel.
   On le sort de l'axe et on borne la largeur du cartel. */
@media (max-width: 1279px) {
  /* Même ancrage qu'en pleine largeur : au bas de la PHOTO, pas de la fenêtre. Cette règle
     datait du bouton centré et le ramenait sous le cadre. */
  .acquire { left:auto; right:calc(var(--hud-pad) + 7rem); transform:none;
    bottom:calc(100% - var(--frame-t, 0px) - var(--frame-h, 100%) + var(--hud-in-y) + var(--dock-lift)); }
  .cartel { max-width:calc(100% - 2*var(--hud-pad) - 14rem); }
}

@media (max-width: 680px) {
  /* Mobile : pas de bande latérale (elle mangeait un tiers de l'écran pour rien).
     La galerie devient une grille plein écran, qu'on ouvre et referme au bouton « Galerie ». */
  .bottomdock { left:0; right:0; top:0; bottom:0; width:auto; transform:translateY(14px);
    background:rgba(6,6,5,0.97); border-right:0; }
  body.dock-on .bottomdock { transform:none; }
  .bottomdock::before { display:none; }

  .bottomdock { flex-direction:column; }
  /* Le bandeau des séries démarre SOUS le bouton « Galerie » (qui est en position fixe
     au-dessus) : sans cette réserve, les deux se superposaient. */
  .seriesbar { flex:0 0 auto; flex-direction:row; gap:6px; overflow-x:auto; overflow-y:hidden;
    border-right:0; border-bottom:1px solid var(--line);
    padding:calc(var(--hud-pad) + 4.6rem) var(--hud-pad) 0.7rem; }
  .serieslist__t { display:none; }
  .serieitem { width:auto; flex:0 0 auto; border-left:0; border-bottom:2px solid transparent; padding:0.4rem 0.7rem; }
  .serieitem[aria-current="true"] { border-left-color:transparent; border-bottom-color:var(--ambre); }
  /* DEUX colonnes, pas trois. À trois, une vignette fait environ 115 px de large sur un iPhone :
     on ne voit plus la photo, on voit une miniature de planche-contact. À deux, elle passe à
     ~180 px, et la légende dessous devient lisible. On perd de la densité, on gagne le sujet. */
  .filmstrip { display:grid; grid-template-columns:repeat(2,1fr); gap:0.9rem 0.7rem; align-content:start;
    padding:var(--hud-pad); background:var(--noir); }
  .frame { width:auto; }
  /* hauteur EXACTE du viewport visible (corrige le bas masqué par la barre iOS) */
  body { height:100dvh; }
  .viewfinder { min-height:0; }                     /* le viseur remplit, la nav basse reste visible */
  .filmstrip { padding-bottom:calc(6px + env(safe-area-inset-bottom)); }
  .cookiebar { padding-bottom:calc(0.6rem + env(safe-area-inset-bottom)); }
  .dial { bottom:var(--hud-pad); }
  .fitbtn   { right:calc(var(--hud-pad) + 16px); bottom:calc(var(--hud-pad) + 90px); }
  .ctl--cart { right:calc(var(--hud-pad) + 16px + env(safe-area-inset-right));
               bottom:calc(var(--hud-pad) + 148px + env(safe-area-inset-bottom)); }
  .reticle .af { display:none; }
  .hud__top { padding:0; }
  /* la largeur s'arrête avant la molette : l'EXIF passait dessous */
  .cartel { left:calc(var(--hud-pad) + 1rem); bottom:calc(var(--hud-pad) + var(--dock-lift) + 3.6rem);
    max-width:calc(100% - 2*var(--hud-pad) - 7rem); }
  /* Sur mobile, « Acquérir » est ramené en pleine largeur SOUS le cartel : centré, il
     chevauchait l'EXIF et le bouton Info. */
  .acquire { left:var(--hud-pad); right:calc(var(--hud-pad) + 5.6rem); transform:none;
    bottom:var(--hud-pad); width:auto; justify-content:center; }
  .cartel { bottom:calc(var(--hud-pad) + 3.4rem); }
  .dial { width:78px; height:78px; }
  /* la molette reste posée en bas à droite : on décale « Retour » pour qu'ils ne se chevauchent pas */
  .ov--lcd { left:var(--hud-pad); right:var(--hud-pad); bottom:calc(var(--hud-pad) + 96px); }
  .lcd { width:auto; }
  .config { grid-template-columns:1fr; }
  .config__preview .preview__scene { min-height:230px; }
}

/* Écrans courts (mobile paysage) : éviter l'entassement */
@media (max-height: 560px) {
  .viewfinder { min-height:0; }
  .cartel__title { font-size:clamp(1.4rem,4vw,2rem); }
  .seriesbar { padding-top:5px; padding-bottom:5px; }
}

/* ---------- Aperçu : fond adapté au support ----------
   Sur fond noir, une caisse américaine ou un cadre noir disparaissait. Hors « vue sur un mur »,
   le fond s'éclaircit dès que le support est sombre, pour que le cadre se détache. */
.preview__scene:not(.is-wall) { transition:background 0.35s var(--ease); }
.preview__scene:not(.is-wall).bg-clair { background:linear-gradient(#9b978f,#7e7a73); }
/* sur fond gris, la légende sous l'aperçu doit rester lisible */
.preview__scene.bg-clair + .preview__cap { color:var(--gris-2); }

/* Le bouton « Voir sur un mur » doit se voir : c'est la démonstration du produit. */
#cfgWall { border-color:var(--ambre); color:var(--ambre); background:rgba(232,199,154,0.1); }
#cfgWall[aria-pressed="true"] { background:var(--ambre); color:var(--noir); }

/* ============================================================
   AJOUTS DU 2026-07-29
   · bouton de cadrage : masqué pendant une transaction, et nommé au survol
   · mention de copyright sur la photo
   · tunnel de commande : coordonnées → paiement → remerciement
   ============================================================ */

/* ---------- Bouton de cadrage ----------
   Il vit en z-index 24, donc AU-DESSUS des panneaux (z-index 21) : pendant le panier ou le
   paiement il flottait sur la page, sans rapport avec elle et sans nom au survol. On l'efface
   avec la molette dès qu'un panneau plein écran est ouvert, et on lui donne une étiquette. */
body.panel-full .fitbtn { opacity:0; pointer-events:none; }
/* Le panier est en position FIXE : il flottait donc au-dessus des panneaux ouverts, et venait se
   poser sur le texte du panier lui-même. Il s'efface avec les autres contrôles dès qu'une couche
   s'ouvre — on n'a pas besoin d'un raccourci vers le panier quand on est DANS le panier. */
body.panel-full .ctl--cart, body.menu-open .ctl--cart { opacity:0; pointer-events:none; }
body.ov-open .ctl--cart { opacity:0; pointer-events:none; }

.fitbtn { position:fixed; }
.fitbtn::after {
  content:attr(data-lbl); position:absolute; right:calc(100% + 0.6rem); top:50%; transform:translateY(-50%);
  font-family:var(--mono); font-size:0.75rem; letter-spacing:0.12em; text-transform:uppercase;
  color:var(--blanc); background:rgba(8,8,7,0.86); backdrop-filter:blur(6px);
  border:1px solid var(--line); border-radius:999px; padding:0.4rem 0.7rem; white-space:nowrap;
  opacity:0; pointer-events:none; transition:opacity 0.25s var(--ease); }
.fitbtn:hover::after, .fitbtn:focus-visible::after { opacity:1; }
@media (hover:none) { .fitbtn::after { display:none; } }   /* au tactile, l'étiquette n'a pas de sens */

/* ---------- Mention de copyright sur la photo ----------
   Discrète et toujours présente. Elle DIT la propriété, elle ne la défend pas : la protection
   réelle est côté serveur (filigrane incrusté, master jamais servi) — voir js/viseur.js. */
/* Calée sur la PHOTO (--frame-*, calculé en JS), pas sur la fenêtre : en mode « image entière »
   elle tombait dans la bande noire, à côté du bouton de cadrage, et ne signait plus rien. */
/* 05.08.2026 · elle était en bas à DROITE, coin devenu celui de l'action depuis que « Acquérir »
   y a été déplacé : les deux se chevauchaient. Elle passe en bas à GAUCHE, sous le cartel, dans le
   coin de l'information — c'est bien ce qu'elle est. Le seul coin sans conflit, et le seul qui ait
   du sens pour une signature. */
/* `.stage__mark` — la signature « © Kim B Lucas » en bas à gauche de la scène — a été RETIRÉE
   le 29.08.2026. Elle n'est pas remplacée par un équivalent CSS : elle est remplacée par le
   filigrane incrusté côté serveur, en bas à droite, que `gneiz-image-protection` appose sur
   chaque dérivé depuis que les œuvres sont remasterisées.

   Ce que ça règle : Yan, le 28.08, devant les deux marques — « il y en a déjà un à gauche avec
   le copyright ». Elles disaient la même chose deux fois, et la version CSS était celle qui
   n'engageait à rien : trois clics dans l'inspecteur en venaient à bout, et elle disparaissait
   entièrement d'un « Enregistrer sous ». Le mémo qui vivait ici le reconnaissait déjà —
   « le filigrane CSS l'ÉNONÇAIT sans jamais la protéger ».

   ⚠️ Elle redevient nécessaire si le filigrane est éteint dans les réglages, ou si une œuvre
   est téléversée SOUS le plafond (pas de `-scaled`, donc pas de dérivé à marquer : le plugin
   le dit et l'écran le compte). La scène serait alors sans mention d'auteur. */

/* Les photos ne se sélectionnent ni ne se glissent. */
.stage__img, .story__img, .filmstrip img, .serie__img img, .config__preview img,
/* -webkit-touch-callout:none — LA propriété qui compte sur iPhone. Sans elle, un appui long sur
   une photo ouvre le menu « Enregistrer l'image », et le visiteur repart avec le fichier en deux
   secondes, sans jamais toucher au clic droit. Elle n'existe que sur WebKit, elle ne coûte rien
   ailleurs.
   Ce que ça ne fait PAS : empêcher une capture d'écran, l'inspecteur, ou l'appel direct de l'URL
   du fichier. Le verrou réel est côté serveur, il est spécifié dans le dossier de transmission. */
img, .stage__img, .story__img, .filmstrip img, .serie__img img, .config__preview img {
  -webkit-touch-callout:none; }
.cartpage__item img, .recap__list img, .done__list img { user-select:none; -webkit-user-drag:none; }

/* ---------- Tunnel de commande ---------- */
.checkout { max-width:1040px; margin:0 auto; }
.checkout--pay, .checkout--done { max-width:680px; }

/* Fil d'étapes */
.steps { list-style:none; display:flex; flex-wrap:wrap; gap:0.5rem 1.6rem; margin-bottom:2.2rem;
  font-family:var(--mono); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase; color:var(--gris); }
.steps__i { display:inline-flex; align-items:center; gap:0.5rem; }
.steps__i span { width:22px; height:22px; display:grid; place-items:center; border-radius:999px;
  border:1px solid var(--line); font-size:0.75rem; }
.steps__i.is-on { color:var(--ambre); }
.steps__i.is-on span { border-color:var(--ambre); color:var(--ambre); }
.steps__i.is-done { color:var(--gris-2); }
.steps__i.is-done span { border-color:var(--vert); color:var(--vert); }

.checkout__grid { display:grid; grid-template-columns:1fr 340px; gap:clamp(1.5rem,4vw,3rem); align-items:start; }
.checkout__form { display:flex; flex-direction:column; gap:1.6rem; }
.checkout__form .opt { border:0; }
.checkout__form legend { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.14em;
  text-transform:uppercase; color:var(--ambre); margin-bottom:0.9rem; }
.checkout__form .field { margin-bottom:0.9rem; }
.checkout__form .field em { font-style:normal; color:var(--gris); text-transform:none; letter-spacing:0; }
.fieldrow { display:grid; grid-template-columns:1fr 1fr; gap:0.9rem; }
.field--np { max-width:120px; }
/* Même seuil de 16 px que les champs de saisie, voir la note plus haut. Une liste déroulante
   déclenche le zoom exactement comme un champ texte. */
.field select { background:var(--surface); border:1px solid var(--bord-champ); color:var(--blanc);
  font-family:var(--sans); font-size:1rem; padding:0.7rem 0.8rem; border-radius:2px; }
.field select:focus { outline:none; border-color:var(--ambre); }

/* ---------- Listes déroulantes que le thème ne pose pas — #115 ----------
   Le thème habille SES champs (`.field select`, juste au-dessus) et laisse nus
   ceux que WooCommerce fabrique : la fiche d'un produit variable, le formulaire
   d'avis, le tunnel. Ces listes gardent donc leur rendu natif.

   Mesuré le 20.08.2026 sur la fiche du produit de test, en production :

       #pa_format  color: rgb(0, 0, 0)   background-color: rgba(0, 0, 0, 0)
       body        background-color: rgb(5, 5, 5)

   Ce n'est pas un contraste faible, c'est du **noir sur noir** : le client ne
   voit pas ce qu'il a choisi. Et il choisissait quand même — la valeur était
   bien retenue, le panier recevait la bonne variation. C'est ce qui a fait
   prendre le défaut pour une perte d'enregistrement (#115) alors que rien ne se
   perdait : l'écran mentait, pas la base.

   On aligne ces listes sur le traitement que le thème donne déjà aux siennes,
   plutôt que d'inventer une seconde apparence. La spécificité reste sous
   `.field select`, qui continue donc de l'emporter là où il s'applique.

   ⚠️ `font-size:1rem` n'est pas décoratif : sous 16 px, iOS zoome à l'ouverture
   de la liste et ne dézoome pas en la refermant. Même seuil que les champs de
   saisie, pour la même raison. */
select { background:var(--surface); border:1px solid var(--bord-champ); color:var(--blanc);
  font-family:var(--sans); font-size:1rem; padding:0.7rem 0.8rem; border-radius:2px;
  min-height:44px; }
select:focus { outline:none; border-color:var(--ambre); }
/* Le menu déroulant lui-même. Sans cette règle il suit les couleurs du système,
   qui redonnent du texte sombre sur fond sombre chez qui utilise un thème clair. */
select option { background:var(--surface); color:var(--blanc); }

/* ⚠️ WooCommerce pose SA règle sur la liste des variations, avec une spécificité
   que la règle nue ci-dessus ne peut pas atteindre :
   `.woocommerce div.product form.cart .variations select` — 0,4,1 contre 0,0,1.
   Elle impose `appearance:none` et un fond transparent.

   Mesuré à l'écran le 20.08.2026, après le déploiement de la règle nue :

       color            rgb(245, 244, 241)   ← appliquée
       background-color rgba(0, 0, 0, 0)     ← PAS appliquée
       font-size 16px · height 45px          ← appliquées

   Le texte devenait donc lisible et le fond restait celui de la page. C'était
   suffisant pour lire, et faux par rapport aux champs du thème, qui portent
   `--surface`. On ne rattrape que le fond, au même sélecteur : dupliquer le
   reste créerait deux endroits à tenir en accord pour rien. */
.woocommerce div.product form.cart .variations select { background:var(--surface); }
.form__err { color:var(--rouge); font-size:0.85rem; margin-top:0.6rem; }

/* Mode de livraison */
.shipopt { display:grid; grid-template-columns:auto 1fr auto; gap:0.3rem 0.8rem; align-items:center;
  border:1px solid var(--ambre); border-radius:2px; padding:0.9rem 1rem; cursor:pointer;
  background:rgba(232,199,154,0.06); }
.shipopt input { accent-color:var(--ambre); grid-row:1 / span 2; }
.shipopt__t { font-size:1.05rem; }
.shipopt__d { grid-column:2; font-family:var(--mono); font-size:0.78rem; letter-spacing:0.08em;
  text-transform:uppercase; color:var(--gris-2); }
.shipopt__p { grid-row:1 / span 2; font-family:var(--mono); color:var(--blanc); }

/* Récapitulatif */
.checkout__recap { border:1px solid var(--line); border-radius:2px; padding:1.4rem;
  background:var(--surface); position:sticky; top:1.5rem; }
.recap__t { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--gris-2); margin-bottom:1rem; }
.recap__list { list-style:none; margin-bottom:1.2rem; }
.recap__list li { display:flex; gap:0.8rem; align-items:center; padding:0.7rem 0; border-top:1px solid var(--line); }
.recap__list img { width:56px; height:40px; object-fit:cover; flex:0 0 auto; }
.recap__meta { flex:1 1 auto; min-width:0; }
.recap__meta .t { display:block; font-family:var(--serif); font-size:1rem; }
.recap__meta .o { display:block; font-family:var(--mono); font-size:0.75rem; letter-spacing:0.06em;
  text-transform:uppercase; color:var(--gris-2); margin-top:0.2rem; }
.recap__p { font-family:var(--mono); font-size:0.92rem; white-space:nowrap; }
.recap__line { display:flex; justify-content:space-between; font-family:var(--mono); font-size:0.82rem;
  letter-spacing:0.06em; color:var(--gris-2); padding:0.4rem 0; }
.recap__line--tot { color:var(--blanc); font-size:1rem; border-top:1px solid var(--line);
  margin-top:0.5rem; padding-top:0.8rem; }
.recap__vat { font-size:0.8rem; color:var(--gris-2); margin:0.3rem 0 1.1rem; }
/* --gris tombait à 4,17 sur le fond du tunnel, mesuré. --gris-2 remonte à 7,8. */
.recap__ship { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase;
  color:var(--gris-2); text-align:center; margin-top:0.9rem; }
.btn-ghost--wide { width:100%; margin-top:0.6rem; }

/* ---------- Paiement ---------- */
.pay { border:1px solid var(--line); border-radius:2px; background:var(--surface); padding:clamp(1.2rem,3vw,2rem); }
.pay__sim { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.12em; text-transform:uppercase;
  color:#1a1508; background:var(--ambre); border-radius:2px; padding:0.45rem 0.7rem;
  text-align:center; margin-bottom:1.4rem; }
.pay__head { display:flex; align-items:center; gap:0.9rem; padding-bottom:1.2rem; border-bottom:1px solid var(--line); }
.pay__lock { font-size:1.2rem; }
.pay__t { font-size:1.05rem; }
.pay__by { font-family:var(--mono); font-size:0.76rem; letter-spacing:0.08em; color:var(--gris-2); margin-top:0.2rem; }
.pay__amt { margin-left:auto; font-family:var(--mono); font-size:1.25rem; color:var(--ambre); white-space:nowrap; }
.pay__methods { display:flex; flex-wrap:wrap; gap:0.5rem; margin:1.4rem 0; }
.paym { font-family:var(--mono); font-size:0.78rem; letter-spacing:0.1em; text-transform:uppercase;
  color:var(--gris-2); background:transparent; border:1px solid var(--line); border-radius:2px;
  padding:0.7rem 1rem; cursor:pointer; min-height:44px; transition:all 0.25s var(--ease); }
.paym:hover { color:var(--blanc); border-color:var(--gris-2); }
.paym.is-on { color:var(--ambre); border-color:var(--ambre); background:rgba(232,199,154,0.1); }

/* ---- Code promo (étape de paiement) -----------------------------------------------------
   Le champ et son bouton sur une ligne, et le message dessous sur toute la largeur : la
   réponse de WooCommerce peut faire deux lignes (« ce code expire le… », « minimum de… »),
   et la coincer à côté du bouton la tronquerait au moment où elle sert. */
.pay__promo { display:flex; flex-wrap:wrap; align-items:flex-end; gap:0.6rem;
              margin:1.4rem 0 0.2rem; padding-top:1.2rem; border-top:1px solid var(--line); }
.field--promo { flex:1 1 12rem; }
.pay__promo .btn-ghost { flex:0 0 auto; }
.pay__promomsg { flex:1 0 100%; font-family:var(--mono); font-size:0.78rem; letter-spacing:0.06em;
                 color:var(--vert, #3f9d5a); margin:0; }
.pay__promomsg.is-err { color:var(--rouge, #c0392b); }
/* La remise se lit comme un gain : même ambre que le montant, pour qu'on la voie. */
.recap__line--disc { color:var(--ambre); }
.pay__pane { margin-bottom:1.4rem; }
.pay__pane .field { margin-bottom:0.9rem; }
.pay__pane input[readonly] { color:var(--gris-2); cursor:not-allowed; }
.btn-primary--pay { margin-top:0.4rem; }
.twint { display:flex; align-items:center; gap:1.2rem; }
.twint__qr { width:96px; height:96px; flex:0 0 auto; border:1px solid var(--line); border-radius:2px;
  background:
    repeating-linear-gradient(90deg,var(--blanc) 0 6px,transparent 6px 12px),
    repeating-linear-gradient(0deg,var(--blanc) 0 6px,transparent 6px 12px),
    var(--noir);
  background-blend-mode:difference; opacity:0.75; }

/* ---------- Remerciement ---------- */
.done { text-align:center; }
.done__mark { width:56px; height:56px; margin:0 auto 1.4rem; display:grid; place-items:center;
  border:1px solid var(--vert); border-radius:999px; color:var(--vert); font-size:1.5rem; }
.done__t { font-family:var(--serif); font-weight:400; font-size:clamp(2.4rem,7vw,3.8rem); }
.done__lede { color:var(--gris-2); margin:0.8rem auto 2.2rem; max-width:46ch; line-height:1.6; }
.done__rec { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:1rem;
  border-block:1px solid var(--line); padding:1.4rem 0; margin-bottom:1.8rem; text-align:left; }
.done__rec dt { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.14em; text-transform:uppercase;
  color:var(--gris); margin-bottom:0.35rem; }
.done__rec dd { font-family:var(--mono); font-size:1rem; color:var(--blanc); }
.done__list { list-style:none; text-align:left; margin-bottom:1.8rem; }
.done__list li { display:flex; gap:0.8rem; align-items:center; padding:0.7rem 0; border-top:1px solid var(--line); }
.done__list img { width:56px; height:40px; object-fit:cover; flex:0 0 auto; }
.done__list .recap__meta .t { font-size:1.05rem; }
.done__note { color:var(--gris-2); font-size:1rem; line-height:1.6; margin-bottom:1.8rem; }
.done .btn-primary { max-width:340px; margin:0 auto; }

/* ---------- Tunnel : écrans étroits ---------- */
@media (max-width: 900px) {
  .checkout__grid { grid-template-columns:1fr; }
  .checkout__recap { position:static; order:-1; }   /* on voit ce qu'on paie AVANT de saisir */
}
@media (max-width: 520px) {
  .fieldrow { grid-template-columns:1fr; }
  .field--np { max-width:none; }
  .pay__head { flex-wrap:wrap; }
  .pay__amt { margin-left:0; width:100%; }
  .steps { gap:0.4rem 0.9rem; font-size:0.75rem; }
}

/* ---------- Surcouche légale (2026-07-29) ----------
   Se pose PAR-DESSUS le tunnel de commande, molette et bouton de cadrage compris (z-index 24),
   sans rien fermer dessous. Lire les CGV ne doit pas coûter le panier. */
.legalpop { position:fixed; inset:0; z-index:60; display:grid; place-items:center;
  padding:clamp(1rem,4vw,3rem); animation:fade 0.3s var(--ease); }
.legalpop__scrim { position:absolute; inset:0; background:rgba(3,3,3,0.8); backdrop-filter:blur(6px); }
.legalpop__panel { position:relative; width:min(720px,100%); max-height:min(80vh,760px); overflow-y:auto;
  background:var(--surface); border:1px solid var(--line); border-radius:3px;
  padding:clamp(1.5rem,4vw,2.6rem); box-shadow:0 30px 90px rgba(0,0,0,0.6); }
.legalpop__close { position:sticky; top:0; float:right; margin:-0.4rem -0.4rem 0 0;
  background:rgba(8,8,7,0.85); backdrop-filter:blur(8px); border:1px solid var(--gris-2);
  border-radius:999px; color:var(--blanc); padding:0.5rem 0.9rem; cursor:pointer;
  font-family:var(--mono); font-size:0.76rem; letter-spacing:0.12em; text-transform:uppercase;
  min-height:44px; z-index:2; }

/* Appel à l'action en pied de page de contenu */
.doc__cta { margin-top:2rem; }
.doc__cta .btn-primary--link { display:inline-block; width:auto; padding:0.9rem 1.6rem; text-align:center; }

/* ---------- Galerie de gauche : la série se lit, elle ne se devine plus (2026-07-29) ----------
   La description n'existait que sur la page « Séries ». Depuis la colonne de gauche, on choisissait
   une série sans savoir ce qu'elle raconte. Elle est maintenant sous le titre, en deux lignes. */
.serieitem__desc { font-family:var(--sans); font-size:0.78rem; line-height:1.35; color:var(--gris);
  margin:0.22rem 0 0.3rem; text-align:left;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.serieitem[aria-current="true"] .serieitem__desc { color:var(--gris-2); }

/* Édition close : la série reste consultable, elle cesse d'appeler à l'achat. */
/* `.serieslist__rule` supprimé le 11.08.2026 EN MÊME TEMPS que son texte (js/viseur.js).
   Laisser le style derrière l'élément, c'est exactement ce qui a produit le bloc `.navhint`
   cassé plus haut dans ce fichier : du CSS orphelin que personne ne relit et que rien ne
   signale. Une règle sans élément se supprime le jour même. */
/* Le badge d'état, repris de la page supprimée : c'est lui qui dit d'un coup d'œil
   ce qui est encore achetable. */
.serieitem__badge { margin-left:0.5rem; vertical-align:0.05em; }
.serieitem--closed .serieitem__name { color:var(--gris); }
.serieitem--closed .serieitem__desc { opacity:0.75; }
.serieitem__closed { color:var(--ambre); opacity:0.85; }

@media (max-width: 680px) {
  /* En rangée horizontale, la description ferait exploser la hauteur de la barre. */
  .serieitem__desc { display:none; }
}

/* La surcouche légale n'a plus qu'une sortie, en haut. */



/* ============================================================
   CORRECTIONS MOBILE — 2026-07-29 (relevées sur iPhone 14)
   ============================================================ */
@media (max-width: 680px) {

  /* 1 · Le menu était COUPÉ EN HAUT. Il n'était ancré que par le bas, sans borne haute ni
     hauteur maximale : avec huit entrées, il débordait au-dessus de l'écran et emportait sa
     barre de titre et sa croix de fermeture. On le borne des deux côtés et on le fait défiler. */
  .ov--lcd { top:calc(var(--hud-pad) + env(safe-area-inset-top));
    display:flex; align-items:flex-end; }
  .lcd { width:auto; max-height:100%; overflow-y:auto; -webkit-overflow-scrolling:touch; }
  .lcd__bar { position:sticky; top:0; z-index:2; background:#070907; }
  /* Entrées resserrées : à 1,65 rem, huit lignes ne tenaient pas dans la hauteur disponible. */
  .lcd__lbl { font-size:1.3rem; }
  .lcd__row { padding:0.62rem 0.35rem; gap:0.7rem; }
  .lcd__ig { margin-top:0.6rem; padding:0.6rem 0.7rem; }

  /* 2 · Le bouton « Galerie » restait affiché PAR-DESSUS la galerie ouverte, sans rien faire de
     visible. Il disparaît maintenant, et on referme en tapant à côté d'une vignette. */
  body.dock-on .ctl--gallery { opacity:0; pointer-events:none; }
  /* La réserve en tête de la barre des séries n'a plus à loger ce bouton. */
  .seriesbar { padding-top:calc(var(--hud-pad) + env(safe-area-inset-top) + 0.7rem); }

  /* 3 · « Acquérir » et « Édition close » : le libellé de la série close passe sur deux lignes
     et venait toucher la ligne EXIF juste au-dessus. On dégage le cartel et on centre le texte. */
  .acquire { bottom:calc(var(--hud-pad) + env(safe-area-inset-bottom));
    padding:0.8rem 1rem; font-size:0.75rem; line-height:1.35; text-align:center;
    letter-spacing:0.1em; }
  .acquire__plus { flex:0 0 auto; }
  .cartel { bottom:calc(var(--hud-pad) + env(safe-area-inset-bottom) + 5.4rem); }

  /* 4 · Molette et bouton de cadrage : la barre d'accueil iOS mangeait leur bas. */
  .dial   { bottom:calc(var(--hud-pad) + env(safe-area-inset-bottom)); }
  .fitbtn    { bottom:calc(var(--hud-pad) + env(safe-area-inset-bottom) + 90px); }
  .ctl--cart { bottom:calc(var(--hud-pad) + env(safe-area-inset-bottom) + 148px); }
}

/* Écrans courts en paysage : le menu doit défiler plutôt que déborder. */
@media (max-height: 560px) {
  .ov--lcd { top:var(--hud-pad); display:flex; align-items:flex-end; }
  .lcd { max-height:100%; overflow-y:auto; }
  .lcd__lbl { font-size:1.15rem; }
  .lcd__row { padding:0.5rem 0.35rem; }
}


/* ============================================================
   MENU PLEIN ÉCRAN SUR MOBILE — 2026-07-29
   Le menu flottait au milieu de l'écran, bordé de noir, avec la molette et le bouton de cadrage
   qui continuaient de flotter par-dessus. Sur un téléphone, un menu de huit entrées est une PAGE :
   il prend tout, et sa croix suffit à en sortir.
   ============================================================ */
@media (max-width: 680px) {
  .ov--lcd { inset:0; align-items:stretch; }
  .lcd { width:100%; height:100%; max-height:none; border:0; border-radius:0;
    box-shadow:none; animation:none;
    padding:calc(env(safe-area-inset-top) + 1.1rem) 1.3rem calc(env(safe-area-inset-bottom) + 1.3rem);
    display:flex; flex-direction:column; }
  .lcd__view { flex:1 1 auto; min-height:0; }
  /* La barre de titre reste en tête, la croix reste atteignable au pouce. */
  .lcd__bar { padding-bottom:0.8rem; margin-bottom:0.6rem; }
  .lcd__close { top:calc(env(safe-area-inset-top) + 0.9rem); right:1rem;
    width:44px; height:44px; display:grid; place-items:center; font-size:1.3rem; }
  /* Le menu occupe tout : plus rien ne doit flotter au-dessus. */
  body.menu-open .dial,
  body.menu-open .fitbtn,
  /* Un menu plein écran peut respirer : on revient à des entrées confortables. */
  .lcd__lbl { font-size:1.45rem; }
  .lcd__row { padding:0.85rem 0.35rem; }
  .lcd__ig { margin-top:auto; }
}

/* Aide au premier lancement du diaporama : trois secondes, puis elle s'efface. */
.storyhint { position:absolute; left:50%; bottom:calc(clamp(1.2rem,3vw,2rem) + 3.2rem);
  transform:translate(-50%,6px); z-index:3;
  display:inline-flex; align-items:center; gap:0.5rem; white-space:nowrap;
  font-family:var(--mono); font-size:0.75rem; letter-spacing:0.12em; text-transform:uppercase;
  color:var(--gris-2); background:rgba(8,8,7,0.55); backdrop-filter:blur(6px);
  border:1px solid var(--line); border-radius:999px; padding:0.5rem 0.95rem;
  opacity:0; pointer-events:none; transition:opacity 0.6s var(--ease), transform 0.6s var(--ease); }
.storyhint.on { opacity:0.9; transform:translate(-50%,0); }
.storyhint__k { color:var(--ambre); }
@media (max-width: 400px) { .storyhint { font-size:0.67rem; padding:0.45rem 0.7rem; gap:0.35rem; } }

/* « Continuer mes achats » et rappel du délai, sous le pied du panier. */
.cartpage__ship { font-family:var(--mono); font-size:0.75rem; letter-spacing:0.1em; text-transform:uppercase;
  color:var(--gris); text-align:center; margin-top:0.9rem; line-height:1.5; }



/* ============================================================
   CORRECTIONS MESURÉES — préflight CDP du 2026-08-03, 182 mesures sur 14 largeurs
   ============================================================ */

/* 1 · L'indication de navigation débordait à 320 et 360 px — correction retirée le 11.08.2026
   AVEC son élément : `.navhint` a disparu de la page le 05.08. Ce qui restait ici était la
   moitié basse de la règle, sans sélecteur : une déclaration orpheline dans un `@media`, donc
   du CSS invalide, et un `}` de trop qui déséquilibrait le fichier depuis six jours. Aucun
   navigateur ne le signale. Supprimer un élément ne suffit pas — il faut supprimer TOUTES ses
   règles, y compris celles qui continuent sur la ligne suivante. */

/* 2 · Cases à cocher du tunnel : 13 px mesurés. Le label entier reste la vraie cible, puisqu'il
   enveloppe l'input, mais une case de 13 px se vise mal et se voit à peine. */
.optin input { width:20px; height:20px; }

/* 3 · Boutons « Fermer » des panneaux : 32 px de haut mesurés, sous le seuil de 44. */
.ov__close, .ovclose { min-height:44px; display:inline-flex; align-items:center; }

/* 4 · Configurateur : les pastilles de format et le bouton d'aperçu mural étaient à 30 et 32 px.
   C'est le parcours d'achat, il ne peut pas avoir les cibles les plus petites du site. */
.chip, .preview__btn { min-height:44px; }

/* Derniers écarts relevés à la remesure. La croix du menu tenait sa hauteur de `.ov__close`
   mais gardait 12 px de large : la hauteur seule ne fait pas une cible. */
.ov__close, .ovclose { min-width:44px; justify-content:center; }
.lcd__close { min-width:44px; min-height:44px; }
/* 43 px et 41 px : à un cheveu du seuil, autant le franchir. */
.field input, .field select, .field textarea { min-height:44px; }
.lcd__ig { min-height:44px; }


/* ---- pages reelles, ajoutees au portage ---- */
.page{max-width:52rem;margin:0 auto;padding:clamp(2rem,6vw,5rem) clamp(1rem,4vw,2rem);}
/* ⚠️ MARGE D'ENTRÉE — 4e passe. Yan signale toujours l'espace en haut du panier et
   du checkout comme trop grand. La règle générique `.page` pose `clamp(2rem, 6vw, 5rem)` en
   padding-top : de 32 à 80 px selon la largeur. Sur les pages du tunnel (panier, commande,
   remerciement), on descend à un padding minimal. `!important` pour battre toute règle
   ultérieure — j'ai perdu ce paragraphe deux fois dans les passes précédentes. */
/* La mention des moyens de paiement au panier — posée par kimb-panier-mention-paiement.
   Discrète, sous le bouton d'action ; le lecteur la voit sans qu'elle rivalise avec le total. */
body.woocommerce-cart .kimb-cart-paiements {
  margin: 0.8rem 0 0;
  font-size: 0.85rem;
  line-height: 1.5;
  /* ⚠️ `var(--gris-2)` et NON une couleur en dur. J'avais écrit `#6b6862` — la teinte du
     tunnel CLAIR, où elle rend 5,32:1 sur crème. Sur le fond noir du panier elle tombe à
     **3,67:1**, sous le seuil AA de 4,5. Le jeton maison `--gris-2` (#a6a39d) rend 8,04:1
     sur `--noir` : conforme AA et AAA, et il est déjà utilisé par 67 autres règles.
     Une vérité, un endroit — la palette savait déjà. */
  color: var(--gris-2);
  text-align: right;
}

body.woocommerce-cart .page,
body.woocommerce-checkout .page,
body.woocommerce-order-received .page {
  padding-top: 0.5rem !important;
  padding-bottom: clamp(1rem, 3vw, 2rem);
  max-width: 76rem;
}

.page__ariane{font-size:.8rem;letter-spacing:.08em;text-transform:uppercase;opacity:.6;margin-bottom:2rem;}
.page__ariane a{color:inherit;}
.page__ariane span{margin:0 .4em;}
.page__galerie--bas{margin:4rem 0 0;}

.series{list-style:none;padding:0;margin:2.5rem 0 0;display:grid;gap:2.5rem;}
.series__item h2{margin:0 0 .4rem;}
.series__item p{margin:.2rem 0;}
.series__meta{font-size:.85rem;opacity:.6;}
.series__item--close h2::after{content:" · close";font-size:.7em;letter-spacing:.06em;opacity:.55;text-transform:uppercase;}

.serie__statut{margin:1.5rem 0;padding:.9rem 1.1rem;border-left:2px solid currentColor;opacity:.85;font-size:.95rem;}
.serie__vues{display:grid;gap:3.5rem;margin-top:3rem;}
.serie__vue{margin:0;}
.serie__vue img{width:100%;height:auto;display:block;}
.serie__titre{margin:1rem 0 .2rem;font-size:1.05rem;}
.serie__lieu{margin:0;font-size:.85rem;opacity:.6;}
.serie__phrase{margin:.6rem 0 0;opacity:.85;}
@media(min-width:48rem){.serie__vues{grid-template-columns:repeat(2,1fr);gap:3rem 2.5rem;}}

/* ---------- Plancher de lisibilité — #119 ----------
   Trois libellés du viseur (.hud, .wordmark, .exif) descendaient à 0.62rem, soit
   9,9 px, en chasse fixe, en capitales et interlettrés : la combinaison la plus
   difficile à lire qui existe. Leur plancher passe à 0.75rem — les 12 px que
   l'échelle du 11.08 pose comme un plancher, pas comme une cible.

   Le plancher ne mordait PAS qu'en mobile. `1.15vw` n'atteint 12 px qu'à partir
   de 1043 px de large : entre 680 et 1043 px — la tablette, et une bonne part des
   portables — ces trois libellés s'affichaient sous le plancher sans que le
   régime mobile soit en cause. C'est ce que le relevé du prototype a montré et
   que « appliqué le 11.08 » laissait supposer réglé.

   Et le mobile garde son propre régime, parce que le ticket l'impose : « sur
   mobile (≤ 680 px), +1 px maximum ». 0.68rem = 10,9 px, soit exactement un
   pixel de plus que les 9,9 px d'avant. Y appliquer les 12 px du bureau ferait
   +2,1 px et casserait les mises en page d'un écran déjà dense — le ticket dit
   que ce serait « plus simple et une erreur ». */
@media (max-width: 680px) {
  .hud      { font-size:0.68rem; }
  .wordmark { font-size:0.68rem; }
  .exif     { font-size:0.68rem; }
}

/* ---- Formulaires FluentForms ------------------------------------------------------------------
   Le formulaire officiel de la maison est FluentForms, y compris dans les panneaux du viseur.
   Son balisage n'est pas le nôtre : on l'habille, on ne le remplace pas.

   La raison de ne pas le réécrire tient en une phrase : WP Armour injecte son champ anti-robots
   DANS le formulaire qu'il a rendu, et refuse toute soumission qui ne le porte pas. Un formulaire
   copié à la main est refusé — mesuré le 21.08.2026.

   Les classes viennent de FluentForms et suivent sa version. À repasser au contrôle de recette
   après chaque montée. */

.ff-el-group { margin-bottom: 1rem; }
.ff-el-input--label label {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gris-2);
  margin-bottom: 0.35rem;
  display: block;
}
.ff-el-is-required.asterisk-right label::after { color: var(--ambre); }

/* 16 px minimum : sous ce seuil, Safari sur iPhone zoome au premier appui et la mise en page
   saute. Même règle que partout ailleurs. Ne pas redescendre. */
.ff-el-form-control {
  background: var(--surface);
  border: 1px solid var(--bord-champ);
  border-radius: 2px;
  color: var(--blanc);
  font-family: var(--sans);
  font-size: 1rem;
  padding: 0.7rem 0.8rem;
  width: 100%;
}
.ff-el-form-control:focus { outline: none; border-color: var(--ambre); box-shadow: none; }
textarea.ff-el-form-control { min-height: 7rem; resize: vertical; }

.ff-btn-submit {
  background: var(--ambre);
  border: 1px solid var(--ambre);
  border-radius: 2px;
  color: var(--noir);
  font-family: var(--mono);
  font-size: 0.85rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.85rem 1.4rem;
  width: 100%;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}
.ff-btn-submit:hover { background: var(--blanc); border-color: var(--blanc); }

/* Les messages de FluentForms — succès, erreurs de validation, refus. */
.ff-message-success {
  color: var(--vert);
  font-size: 1.05rem;
  line-height: 1.5;
}
.ff-el-is-error .ff-el-form-control { border-color: var(--rouge); }
.error.text-danger, .ff-errors-in-stack, .ff-el-form-check-label .error {
  color: var(--rouge);
  font-size: 0.9rem;
}
.ff-errors-in-stack { border: 1px solid var(--rouge); border-radius: 2px; padding: 0.7rem 0.9rem; }

/* Le titre que FluentForms réserve aux lecteurs d'écran ne doit pas s'afficher : le panneau a
   déjà son propre titre juste au-dessus. */
.ff_screen_reader_title { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* ---- La sortie « Retour à la galerie » ---------------------------------------------------------
   Posée par `page.php`, donc présente sur des pages qui ne sont pas des pages WooCommerce : les
   textes légaux d'abord. Ses règles vivaient dans `assets/css/woocommerce-blocks.css`, qui n'est
   chargé que dans le tunnel — le même bouton sortait nu partout ailleurs. (21.08.2026) */
.page__galerie { margin:0 0 2rem; display:flex; flex-wrap:wrap; gap:0.6rem; }
.page__galerie a {
  display:inline-block; font-family:var(--mono); font-size:0.76rem; letter-spacing:0.12em;
  text-transform:uppercase; color:var(--gris-2); text-decoration:none;
  border:1px solid var(--line); border-radius:2px; padding:0.6rem 1.1rem;
  transition:color 0.2s, border-color 0.2s;
}
.page__galerie a:hover { color:var(--ambre); border-color:var(--ambre); }

/* ---- La colonne d'un document ------------------------------------------------------------------
   46ch tenait de la page d'un livre : juste pour de la prose, trop étroit pour un texte juridique,
   qui s'y déroule sur trois écrans de plus. 72ch reste dans la mesure lisible (45–75) et rend un
   article d'un seul tenant. Le calque, lui, garde sa largeur propre. (21.08.2026) */
.page .doc { max-width:72ch; }

/* ---- Le panier vide invite à revenir voir ------------------------------------------------------
   WooCommerce y posait son émoji triste, un séparateur en pointillés et « Nouveau dans la
   boutique » — quatre produits dans un habillage qui n'est pas le nôtre. Un panier vide n'est pas
   un échec : c'est quelqu'un qui n'a pas encore choisi. */
.kimb-vide { margin:3rem 0 1rem; }
.kimb-vide p { color:#d8d6d1; line-height:1.65; max-width:52ch; }
.kimb-vide .kimb-vide__note { color:var(--gris-2); font-size:0.95rem; }
.doc__attente { color:var(--gris-2); font-style:italic; }

/* ---- Le bouton d'envoi de FluentForms ----------------------------------------------------------
   `.ff-btn-submit` était déjà habillé en ambre plus haut. FluentForms peint le sien par une
   feuille EN LIGNE, imprimée avec le formulaire donc APRÈS la nôtre, sur un sélecteur de même
   spécificité — `form.fluent_form_1 .ff-btn-submit:not(.ff_btn_no_style)`. À égalité, le dernier
   gagne : le bouton sortait en `#1a7efb`, bleu, au milieu d'un site noir et ambre.

   Deux gestes : la variable qu'il consomme prend notre ambre, et un sélecteur d'un cran plus
   spécifique reprend la couleur du texte, que sa règle fixe en blanc — illisible sur l'ambre.
   (23.08.2026) */
:root { --fluentform-primary: var(--ambre); }
body form.frm-fluent-form .ff-btn-submit:not(.ff_btn_no_style),
body form.fluent_form_1 .ff-btn-submit:not(.ff_btn_no_style),
body form.fluent_form_2 .ff-btn-submit:not(.ff_btn_no_style) {
  background: var(--ambre); border: 1px solid var(--ambre); color: var(--noir);
  font-family: var(--mono); font-size: 0.85rem; letter-spacing: 0.14em; text-transform: uppercase;
  border-radius: 2px; padding: 0.85rem 1.4rem; width: 100%;
}
body form.frm-fluent-form .ff-btn-submit:not(.ff_btn_no_style):hover,
body form.fluent_form_1 .ff-btn-submit:not(.ff_btn_no_style):hover,
body form.fluent_form_2 .ff-btn-submit:not(.ff_btn_no_style):hover {
  background: var(--blanc); border-color: var(--blanc); color: var(--noir);
}

/* ---- Aucun texte ne dépasse son cadre ----------------------------------------------------------
   « EN VENTE » tient dans la pastille ; « ERHÄLTLICH » la remplit au caractère près et sa dernière
   lettre touchait le filet. Deux causes, toutes deux structurelles et non typographiques : la
   pastille pouvait dépasser sa carte, et l'interlettrage ajoute un blanc APRÈS la dernière lettre,
   que le centrage transforme en décalage vers la droite.

   La règle vaut pour toutes les langues, y compris celles qu'on n'a pas encore : une pastille ne
   dépasse jamais sa carte, son texte revient à la ligne plutôt que d'être coupé, et le
   rembourrage de droite compense l'interlettrage. (23.08.2026) */
.badge {
  max-width: 100%;
  box-sizing: border-box;
  padding-inline-end: calc(0.5rem + 0.12em);
  white-space: normal;
  overflow-wrap: anywhere;
}
.serieitem, .serieitem > * { min-width: 0; max-width: 100%; }
.serieitem__name, .serieitem__desc, .cartel__statut { overflow-wrap: anywhere; }

/* ---- La boutique porte l'enveloppe des pages, pas la sienne -------------------------------------
   Depuis `woocommerce.php` (23.08.2026), la fiche d'œuvre, la boutique et les archives de série
   passent par la même enveloppe que « Kim », les CGV ou le panier : `<main class="page">`.

   Une seule chose les distingue : la LARGEUR. `.page` tient à 52 rem, calibré pour une colonne de
   texte suivie à l'œil ; une fiche d'œuvre est en deux colonnes — la photographie à gauche, le
   configurateur à droite — et la boutique aligne quatre cartes. À 52 rem, la photographie tombait
   sous les 300 px et les quatre cartes passaient à deux. La grille de WooCommerce a besoin d'air ;
   on le lui donne ici, une fois, plutôt que de la contraindre carte par carte. */
.page--boutique { max-width: 76rem; }

/* Le titre d'archive rendu par `woocommerce_content()` — « Boutique », « Ralentir ». Il n'avait
   aucun style à lui : il héritait du `h1` du navigateur, en gras et en sans-serif, au-dessus d'une
   grille entièrement composée en Caslon. */
.page--boutique .page-title,
.page--boutique .woocommerce-products-header__title {
  font-family: var(--serif); font-weight: 400; line-height: 1.05;
  font-size: clamp(1.8rem, 4vw, 2.6rem); color: var(--blanc); margin: 0 0 1.75rem;
}

/* Le fil d'Ariane et la sortie tiennent au bord du contenu, pas au bord de l'écran : sur la
   boutique, la grille commence à gauche, et un fil d'Ariane décalé de la première carte donne
   l'impression de deux mises en page superposées. */
.page--boutique .page__ariane { margin-bottom: 1.25rem; }

/* ---- Les onglets de la fiche d'œuvre --------------------------------------------------------
   WooCommerce dessine « Description / Informations complémentaires » en gris clair sur fond
   blanc, avec des coins arrondis et des pseudo-éléments qui simulent des onglets de classeur.
   Posés sur le noir du site, ils formaient la seule zone claire de toute la page — relevé le
   23.08.2026 juste après la mise sous enveloppe.

   On neutralise le décor d'usine (les `::before` / `::after` portent les coins arrondis) et on
   reprend la forme employée partout ailleurs : capitales espacées en mono, un filet sous la
   ligne, l'onglet courant en ambre. */
.page--boutique .woocommerce-tabs { margin-top: 3.5rem; }

/* ⚠️ LA SPÉCIFICITÉ EST LE SUJET, pas la couleur. WooCommerce écrit ses onglets avec
   `.woocommerce div.product .woocommerce-tabs ul.tabs li.active` — cinq classes et trois
   éléments. Une règle en `.page--boutique …` en compte trois : elle perd, et l'onglet courant
   restait blanc sur le noir malgré la déclaration. On reprend donc son propre sélecteur, préfixé
   de `body`, ce qui suffit à passer devant sans recourir à `!important`. Un `!important` gagne
   aussi, mais il rend la règle suivante impossible à écrire. */
body.woocommerce div.product .woocommerce-tabs ul.tabs {
  display: flex; flex-wrap: wrap; gap: 0; margin: 0 0 1.75rem; padding: 0;
  list-style: none; border-bottom: 1px solid var(--line);
}
body.woocommerce div.product .woocommerce-tabs ul.tabs::before,
body.woocommerce div.product .woocommerce-tabs ul.tabs::after,
body.woocommerce div.product .woocommerce-tabs ul.tabs li::before,
body.woocommerce div.product .woocommerce-tabs ul.tabs li::after,
body.woocommerce div.product .woocommerce-tabs ul.tabs li.active::before,
body.woocommerce div.product .woocommerce-tabs ul.tabs li.active::after {
  content: none; display: none; box-shadow: none; border: 0;
}
body.woocommerce div.product .woocommerce-tabs ul.tabs li,
body.woocommerce div.product .woocommerce-tabs ul.tabs li.active {
  margin: 0; padding: 0; background: none; background-color: transparent;
  border: 0; border-radius: 0; box-shadow: none;
}
body.woocommerce div.product .woocommerce-tabs ul.tabs li a,
body.woocommerce div.product .woocommerce-tabs ul.tabs li.active a {
  display: block; padding: 0.7rem 1.2rem 0.75rem; margin-bottom: -1px;
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase;
  font-weight: 400; color: var(--gris-2); text-decoration: none;
  border-bottom: 1px solid transparent;
}
body.woocommerce div.product .woocommerce-tabs ul.tabs li a:hover { color: var(--blanc); }
body.woocommerce div.product .woocommerce-tabs ul.tabs li.active a {
  color: var(--ambre); border-bottom-color: var(--ambre);
}

.page--boutique .woocommerce-tabs .panel > h2 {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.page--boutique .woocommerce-tabs .panel p { color: #d8d6d1; line-height: 1.65; }

/* Le tableau des caractéristiques du deuxième onglet : mêmes filets que la fiche technique
   au-dessus, pour qu'on lise deux fois la même chose de la même façon. */
.page--boutique table.shop_attributes { width: 100%; border-collapse: collapse; }
.page--boutique table.shop_attributes th,
.page--boutique table.shop_attributes td {
  text-align: left; padding: 0.6rem 0; border-bottom: 1px solid var(--line); font-style: normal;
}
.page--boutique table.shop_attributes th {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--gris); font-weight: 400; width: 40%;
}

/* Les autres tirages, en bas de fiche. */
.page--boutique .related.products { margin-top: 3.5rem; }
.page--boutique .related.products > h2 {
  font-family: var(--serif); font-weight: 400; font-size: clamp(1.3rem, 2.6vw, 1.7rem);
  color: var(--blanc); margin: 0 0 1.5rem;
}

/* ---- La loupe de la fiche d'œuvre, et la visionneuse qu'elle ouvre -----------------------------
   B11. `functions.php` déclare `wc-product-gallery-zoom` et `wc-product-gallery-lightbox` : la
   fiche charge donc PhotoSwipe 4.1.1 et sa peau d'usine, six feuilles avant la nôtre. Relevé le
   23.08.2026 sur `/tirage/ralentir-le-ponton/`, dans `woocommerce.css` 11.0.0 :

     .woocommerce div.product div.images .woocommerce-product-gallery__trigger {
       background:#fff; border-radius:100%; height:36px; width:36px; … }

   — un rond blanc de 36 px posé sur la photographie, ses deux traits en `#000`, seule zone claire
   d'un site dont le fond vaut #050505. La visionneuse par-dessous vient du même carton : barre
   noire à 50 %, compteur blanc à 75 % d'opacité, boutons découpés dans un PNG.

   ⚠️ LE DÉCLENCHEUR N'EST PAS DANS LE HTML SERVI, et c'est le premier piège de ce chantier.
   `curl` sur six fiches du catalogue rend `woocommerce-product-gallery__trigger` **0 fois** :
   c'est `single-product.min.js` qui l'insère au chargement — `this.$target.prepend('<a href="#"
   role="button" class="woocommerce-product-gallery__trigger" …>')`. Vérifier qu'il est habillé en
   cherchant la classe à la source donnera donc toujours zéro, quelle que soit la règle écrite.

   ⚠️ LA SPÉCIFICITÉ EST LE SUJET, comme pour les onglets plus haut. Le sélecteur d'usine compte
   quatre classes et deux éléments ; une règle plus courte perd, même écrite après. On reprend donc
   le sien préfixé de `body`, ce qui suffit à passer devant sans recourir à `!important` — un
   `!important` gagne aussi, mais il rend la règle suivante impossible à écrire. */

/* ⚠️ LE FILET EST POSÉ EN `box-shadow`, PAS EN `border`, et ce n'est pas une préférence.
   WooCommerce met ce bouton en `box-sizing:content-box` avec `width:36px; height:36px`. Une
   bordure de 1 px porterait la boîte à 38 px, alors que l'anneau et le manche de la loupe sont
   posés en pixels absolus dans cette boîte — `left:9px/top:9px` pour l'anneau, `left:22px/top:19px`
   pour le manche. Un filet en ombre intérieure ne déplace rien.

   Pour la même raison on ne touche NI à `font-size`, NI à l'épaisseur de l'anneau : le bouton est
   placé par `top:.5em; right:.5em` sur un `font-size:2em`, soit 16 px de marge — changer la taille
   de police le déplacerait de la même quantité. Recolorer suffit ; toute retouche de géométrie se
   paierait en glyphe désaligné. */
body.woocommerce div.product div.images .woocommerce-product-gallery__trigger {
  background: rgba(5,5,5,0.72);
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px var(--line);
  color: var(--gris-2);
  transition: color 0.2s, box-shadow 0.2s, background 0.2s;
}
body.woocommerce div.product div.images .woocommerce-product-gallery__trigger:hover,
body.woocommerce div.product div.images .woocommerce-product-gallery__trigger:focus-visible {
  background: rgba(5,5,5,0.9);
  box-shadow: inset 0 0 0 1px var(--ambre);
  color: var(--ambre);
  outline: none;
}
/* L'anneau et le manche sont dessinés par WooCommerce en `#000`, pensés pour un fond blanc. En
   `currentColor` ils suivent la couleur du bouton, et le survol n'a plus qu'une seule chose à
   déclarer. */
body.woocommerce div.product div.images .woocommerce-product-gallery__trigger::before {
  border-color: currentColor;
}
body.woocommerce div.product div.images .woocommerce-product-gallery__trigger::after {
  background: currentColor;
}

/* Le fond de la visionneuse. PhotoSwipe l'a en `#000` ; le site est en #050505. Deux noirs dans un
   même produit se voient au moment où l'un s'ouvre par-dessus l'autre — il n'y en a qu'un. */
body .pswp__bg { background: var(--noir); }

/* La barre du haut et la légende sortaient en `rgba(0,0,0,.5)`, ce qui trace deux arêtes nettes en
   travers de la photographie, à 44 px du haut et au bas de l'écran. On reprend la forme que
   l'introduction emploie déjà pour poser du texte sur une image (`.story__veil`) : un voile qui
   s'éteint, sans bord.

   ⚠️ Les deux sélecteurs sont nécessaires. PhotoSwipe repeint la barre et la légende sous
   `.pswp__ui--fit .pswp__caption` — deux classes. `body .pswp__caption` n'en compte qu'une et
   perdrait dès que l'interface passe en mode ajusté. */
body .pswp__top-bar,
body .pswp__ui--fit .pswp__top-bar {
  background: linear-gradient(rgba(5,5,5,0.88), rgba(5,5,5,0));
}
body .pswp__caption,
body .pswp__ui--fit .pswp__caption {
  background: linear-gradient(rgba(5,5,5,0), rgba(5,5,5,0.92));
}

/* La légende, dans la forme du cartel : le texte en sans, la mention technique en capitales mono.
   ⚠️ `.pswp__caption small` vaut `font-size:11px` chez PhotoSwipe — sous le plancher du ticket
   #119, qui interdit toute police en dessous de 12 px. `0.75rem` fait exactement 12 px. */
body .pswp__caption .pswp__caption__center {
  max-width: 52ch;
  padding: 1.1rem 1.25rem 1.3rem;
  font-family: var(--sans);
  font-size: 0.95rem;
  line-height: 1.55;
  color: #d8d6d1;
}
body .pswp__caption .pswp__caption__center small {
  display: block;
  margin-top: 0.4rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gris-2);
}

/* Le compteur « 2 / 5 ». PhotoSwipe l'écrit en blanc à 75 % d'opacité : un texte transparent
   ramasse ce qui passe derrière lui, et la photographie change à chaque vue. La maison atténue en
   choisissant une couleur, pas en laissant passer le fond. */
body .pswp__counter {
  font-family: var(--mono);
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  color: var(--gris-2);
  opacity: 1;
  padding: 0 0.9rem;
}

/* Les boutons. Les six glyphes sont découpés dans un seul PNG de 264 × 88 px. On l'éteint et on
   redessine au trait de 1 px, l'épaisseur employée partout ailleurs — un sprite ne se recolore pas.

   ⚠️ ICI, ET SEULEMENT ICI, `!important` EST LA SEULE ISSUE — et c'est mesuré, pas supposé.
   `photoswipe.min.css` (WooCommerce 11.0.0, chargé bien avant nous) écrit :

       button.pswp__button { box-shadow:none!important;
                             background-image:url('default-skin/default-skin.png')!important }

   Aucune spécificité ne passe devant un `!important` : c'est le seul point du CSS où allonger le
   sélecteur ne sert à rien. Relevé le 23.08.2026 — l'adresse relative se résout en
   `…/assets/css/photoswipe/default-skin/default-skin.png`, qui rend **200, 547 octets**. Le sprite
   se peint donc réellement, et sans ce `!important` en retour on obtiendrait DEUX glyphes
   superposés par bouton, le nôtre et celui d'usine.

   La contrepartie est réelle et assumée : la ligne suivante qui voudra rendre un fond à ces
   boutons devra elle aussi porter `!important`. On le limite donc à `background-image`, la seule
   propriété verrouillée ; la couleur, l'opacité et le reste passent par la spécificité.

   ⚠️ `--share` GARDE SON SPRITE À DESSEIN. La fiche pose `photoswipe_options` avec
   `"shareEl": false` : le bouton est dans le balisage, PhotoSwipe ne l'affiche pas. L'éteindre le
   rendrait invisible — et non « d'usine » — le jour où quelqu'un rouvre l'option, sans rien pour
   signaler la cause. Un bouton mal habillé se diagnostique ; un bouton nu, non. */
body .pswp__button:not(.pswp__button--share) {
  background-color: transparent;
  background-image: none !important;
  color: var(--gris-2);
  opacity: 1;
  transition: color 0.2s;
}
body .pswp__button:not(.pswp__button--share):hover { color: var(--ambre); }
/* PhotoSwipe ne signale le focus clavier que par `opacity:1` — invisible une fois l'opacité fixée
   à 1 partout. Le cadre est en `outline` et non en `box-shadow` : la même règle ci-dessus verrouille
   `box-shadow:none!important` sur ces boutons. `outline-offset` négatif garde le cadre dans les
   44 px du bouton, qui se touchent deux à deux dans la barre. */
body .pswp__button:focus-visible {
  color: var(--ambre);
  outline: 1px solid var(--ambre);
  outline-offset: -8px;
}

/* Fermer : deux traits croisés, comme le « Fermer × » des panneaux du viseur. */
body .pswp__button--close::before,
body .pswp__button--close::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 15px; height: 1px; background: currentColor;
}
body .pswp__button--close::before { transform: translate(-50%,-50%) rotate(45deg); }
body .pswp__button--close::after  { transform: translate(-50%,-50%) rotate(-45deg); }

/* Précédent / suivant : un chevron, obtenu en ne gardant que deux côtés d'un carré. Le `::before`
   d'usine portait aussi une plaque `rgba(0,0,0,.3)` de 32 × 30 px derrière la flèche ; sur un fond
   déjà noir elle ne servait qu'à faire une tache. */
body .pswp__button--arrow--left::before,
body .pswp__button--arrow--right::before {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 13px; height: 13px;
  background: none;
  border: 1px solid currentColor;
  border-width: 1px 1px 0 0;
}
body .pswp__button--arrow--left::before  { transform: translate(-50%,-50%) rotate(-135deg); }
body .pswp__button--arrow--right::before { transform: translate(-50%,-50%) rotate(45deg); }

/* Agrandir / réduire : un « + » dont la barre verticale disparaît une fois l'image agrandie, ce
   qui en fait un « − ». C'est exactement ce que faisait le PNG en décalant sa fenêtre de 44 px. */
body .pswp__button--zoom::before,
body .pswp__button--zoom::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  background: currentColor; transform: translate(-50%,-50%);
}
body .pswp__button--zoom::before { width: 15px; height: 1px; }
body .pswp__button--zoom::after  { width: 1px; height: 15px; }
body .pswp--zoomed-in .pswp__button--zoom::after { display: none; }

/* Plein écran : deux équerres opposées. La forme dit l'écartement, ce qu'une double flèche ne fait
   qu'à condition d'être lue. */
body .pswp__button--fs::before,
body .pswp__button--fs::after {
  content: ""; position: absolute; width: 6px; height: 6px;
  border: 1px solid currentColor;
}
body .pswp__button--fs::before { top: 15px; left: 15px; border-width: 1px 0 0 1px; }
body .pswp__button--fs::after  { bottom: 15px; right: 15px; border-width: 0 1px 1px 0; }

/* L'attente de chargement : le seul moment où la visionneuse dit quelque chose avant d'avoir
   l'image. Le blanc d'usine y est le geste le plus visible de tout l'écran ; l'ambre est la couleur
   que le site emploie déjà pour « il se passe quelque chose ». */
body .pswp--css_animation .pswp__preloader__donut {
  border-color: var(--ambre);
  border-left-color: transparent;
  border-bottom-color: transparent;
}

/* ---- La fiche d'une œuvre vit SOUS le viseur ----------------------------------------------
   Décision de Yan du 23.08 : `/tirage/<oeuvre>/` sert le viseur ouvert sur la photographie, et
   la fiche complète dessous, dans le même document.

   ⚠️ **Elle n'est pas masquée, elle est en dessous.** Un bloc invisible à l'œil mais présent
   dans le document est du camouflage aux yeux d'un moteur. Ici le visiteur l'atteint en faisant
   défiler, exactement comme un robot la lit — et sans JavaScript, le viseur ne s'affiche pas :
   la fiche est alors tout ce qui reste, et la page continue de vendre.

   `.viewfinder` porte `flex:1 1 auto; min-height:80vh` : il prend le premier écran, la fiche
   suit. Rien à forcer, le flux fait le travail. On ajoute seulement le filet qui sépare les deux
   et l'air qu'il faut pour que la seconde ne commence pas collée à la première. */
.page--sous-viseur {
  border-top: 1px solid var(--line);
  padding-top: clamp(2.5rem, 6vw, 4.5rem);
}

/* ⚠️ SUR UNE FICHE, LE VISEUR PREND LE PREMIER ÉCRAN — mesuré, pas supposé.
   `.viewfinder` tire sa hauteur de `flex:1 1 auto` dans un `body` en colonne : sur l'accueil il
   est seul, donc il prend tout. Sur une fiche il partage la colonne avec la fiche, qui mesure
   2818 px de contenu — relevé au navigateur. Le flex lui a tout donné et le viseur s'est
   retrouvé à **0 px de haut** : ses enfants sont en `position:absolute` et ne portent aucune
   hauteur. La page servait donc la fiche seule, alors que le balisage du viseur était bien là.

   `min-height` ne suffit pas : une règle de media query le remet à 0 sur les écrans courts. On
   fixe donc la hauteur, et on sort le viseur du partage flex — il n'a plus à négocier. */
body.single-product .viewfinder {
  flex: 0 0 auto;
  height: 88vh;
  height: 88dvh;
  min-height: 88vh;
  min-height: 88dvh;
}

/* ⚠️ Le curseur-loupe du viseur ne déborde pas sur la fiche : on y achète, et un curseur
   invisible sur un bouton « Ajouter au panier » se paie en commandes perdues. */
body.lenscursor .page--sous-viseur,
body.lenscursor .page--sous-viseur * { cursor: auto; }
body.lenscursor .page--sous-viseur a,
body.lenscursor .page--sous-viseur button,
body.lenscursor .page--sous-viseur .button { cursor: pointer; }

/* ---- « Acquérir ce tirage » reconnaît ce qui est déjà au panier ---------------------------
   Question de Yan du 23.08 : faut-il changer le bouton quand le tirage est déjà dedans ?

   Réponse retenue : **on informe, on ne ferme pas**. Le bouton ouvre le configurateur ; prendre
   la même photographie dans un autre format après en avoir mis une au panier est la seconde
   commande la plus plausible de ce site, et la refuser coûterait plus qu'elle ne protège.

   L'ambre plein signale « à acquérir ». Dans l'état « déjà au panier », il passe au contour :
   l'action reste offerte, elle cesse d'être ce que la page pousse à faire. La hiérarchie dit
   l'état ; le texte le nomme. */
.acquire--au-panier {
  background: transparent;
  color: var(--ambre);
  border: 1px solid var(--ambre);
}
.acquire--au-panier:hover,
.acquire--au-panier:focus-visible {
  background: var(--ambre);
  color: var(--noir);
}

/* La seconde ligne, plus discrète : ce qu'on peut encore faire, sans crier. Elle passe sous la
   première dès qu'il n'y a plus la place — jamais coupée, jamais débordante. */
.acquire__suite {
  display: block;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  opacity: 0.72;
  margin-top: 0.2rem;
  overflow-wrap: anywhere;
}

/* ---- LA PAGE COMMANDE PASSE SUR FOND CLAIR ------------------------------------------------
   Décision de Yan, 23.08.2026, précisée après un premier essai : « le panier doit revenir comme
   avant, uniquement la page commande en clair ».

   Le premier jet avait basculé les deux. Le panier n'en avait pas besoin : on y relit trois
   lignes et un total, et le noir lui va. **La commande, elle, est un formulaire long** — adresse,
   pays, code postal, moyen de paiement. C'est là qu'on saisit, qu'on se trompe, qu'on relit. Un
   fond clair y sert à quelque chose ; sur le panier il ne servait qu'à faire deux écrans
   différents à la suite.

   La page garde son fond sombre : le conteneur flotte dessus.

   ⚠️ **On ne redéfinit PAS les variables de la palette.** C'était la solution élégante — échanger
   les rôles et laisser tout suivre. Elle casse sur les boutons : l'ambre y sert de FOND avec
   `--noir` en texte ; inverser `--noir` mettrait du texte clair sur de l'ambre. Les rôles ne sont
   pas symétriques, donc l'échange global ne l'est pas non plus. On nomme chaque surface. */

/* ⚠️ SUR LES PAGES DE COMMANDE, LE BODY DEVIENT CLAIR — RÈGLE PRIMORDIALE.
   Sans cela, le titre « Validation de la commande » se retrouve noir sur noir : le viseur
   sert son fond `body { background: var(--noir) }` à tout le site, et l'ancien correctif
   n'éclaircissait que le bloc du tunnel — pas ce qui vit autour, ni le titre, ni les
   boutons de retour, ni la marge d'entrée. Mesuré sur la capture de Yan le 24.08.2026.
   ---------------------------------------------------------------------------------- */
body.woocommerce-checkout,
body.woocommerce-order-received {
  background: #fbfaf8;
  color: #1c1b19;
}

/* ⚠️ NEUTRALISER LE FOND DES CONTENEURS DU TUNNEL — À LA RACINE.
   Vu sur la capture de Yan (24.08.2026) : les cartes de paiement étaient bien claires,
   mais LE POURTOUR autour d'elles restait NOIR — le conteneur du bloc de paiement lui-
   même portait un fond sombre hérité. Impossible à attraper par les seules classes de
   surface : ce parent n'a ni `radio-control` ni `payment-method` dans son nom, c'est un
   `wp-block-woocommerce-checkout-payment-block`, et il en existe une dizaine de variantes
   selon le bloc (contact, shipping, billing, payment, order-summary).

   On rend TRANSPARENT tout descendant de `body.woocommerce-checkout` dont la classe
   contient `wp-block-woocommerce` ou `wc-block-checkout__`. Les cartes qu'on veut peindre
   en clair l'emportent par leur règle nominative plus bas. Le fond crème du body devient
   alors la couleur ambiante — cohérent avec l'esprit du tunnel. */
/* ⚠️ NEUTRALISATION FINALE — tuiles de paiement fermées restaient noires (24.08.2026).
   Yan a envoyé une troisième capture après mes deux passes : les cartes de paiement
   non-sélectionnées (Visa, Mastercard, PostFinance, Klarna quand une autre est cochée)
   gardaient un fond NOIR bien qu'ayant reçu leur règle blanche.

   La cause : ces cartes vivent dans des conteneurs anonymes (`<div>` sans classe qui
   commence par `wp-block-woocommerce` ou `wc-block-checkout__`), et ces `<div>` héritent
   du fond sombre du thème. Ma règle par attribut ne les attrapait pas.

   Le vrai marteau : sur les pages de tunnel, TOUS les `div`, `section`, `fieldset`, `ul`,
   `li` deviennent transparents. Le fond crème du body (déjà posé) remonte. Les surfaces
   qui doivent rester blanches — les cartes de paiement/livraison, la boîte du tunnel —
   ont leur règle nominative plus bas, qui l'emporte par spécificité. */
body.woocommerce-checkout :is(div, section, fieldset, ul, li),
body.woocommerce-order-received :is(div, section, fieldset, ul, li) {
  background: transparent;
  background-color: transparent;
}

/* ⚠️ ET LES CARTES REDEVIENNENT BLANCHES — ne pas oublier cette moitié.
   La neutralisation ci-dessus rend TOUS les conteneurs transparents. Il faut remettre
   les surfaces qu'on veut voir en blanc, sinon les tuiles héritent du crème et se
   confondent avec le fond de la boîte. Mesuré au DOM le 24.08.2026 : les labels
   `.wc-block-components-radio-control__option` non sélectionnés portaient `rgb(23,23,21)`.

   ⚠️ CES RÈGLES ONT PORTÉ `!important` PENDANT UNE NUIT, ET C'ÉTAIT UNE FACILITÉ.
   Elles le portaient après trois passes ratées, écrites au jugé plutôt qu'à la mesure.
   Le banc `LeImportantResteOuIlEstInevitable` les a refusées à raison : la maison
   n'accepte `!important` qu'à UN seul endroit, sur `background-image`, parce que la
   feuille d'usine de PhotoSwipe en pose un qu'aucune spécificité ne franchit.

   Ici rien ne l'imposait. La règle concurrente est une règle ordinaire du thème : un
   sélecteur préfixé de `body` et suivi de sa classe pèse (0,2,1) et arrive plus loin dans
   la feuille — il passe devant sans forcer. `:where()` n'est pas employé : il annulerait
   justement le poids qu'on cherche.

   Le `!important` retiré, la mesure au DOM reste `rgb(255,255,255)` sur les cinq cartes. */
body.woocommerce-checkout .wc-block-components-radio-control__option,
body.woocommerce-checkout .wc-block-components-radio-control-accordion-option,
body.woocommerce-checkout label.wc-block-components-radio-control__option,
body.woocommerce-order-received .wc-block-components-radio-control__option,
body.woocommerce-order-received .wc-block-components-radio-control-accordion-option {
  background-color: #ffffff;
  color: #1c1b19;
  border-color: #e2ded6;
}

/* L'option cochée garde sa teinte ambrée d'accent — bordure ambre, fond très légèrement
   teinté. Elle suit les mêmes règles de poids que ci-dessus. */
body.woocommerce-checkout .wc-block-components-radio-control__option-checked,
body.woocommerce-checkout .wc-block-components-radio-control-accordion-option--checked-option-highlighted {
  background-color: rgba(232, 199, 154, 0.10);
  border-color: var(--ambre);
}

body.woocommerce-checkout .page,
body.woocommerce-order-received .page {
  color: #1c1b19;
  /* ⚠️ La marge d'entrée passe de 5rem à 1.5rem : Yan a signalé qu'entre le bord haut
     et « Retour à la galerie » c'était « beaucoup trop grand ». Vaut pour toutes les
     pages du tunnel — commande, remerciement, panier — et pas seulement le checkout. */
  padding-top: clamp(1rem, 2.5vw, 1.5rem);
}
body.woocommerce-cart .page {
  padding-top: clamp(1rem, 2.5vw, 1.5rem);
}

body.woocommerce-checkout .wc-block-checkout {
  background: #fbfaf8;
  color: #1c1b19;
  border: 1px solid #e2ded6;
  border-radius: 3px;
  padding: clamp(1.2rem, 3vw, 2.2rem);
}

body.woocommerce-checkout .wc-block-checkout :is(h1,h2,h3,p,span,td,th,label,legend,li,a,strong,em) {
  color: #1c1b19;
}

body.woocommerce-checkout .wc-block-checkout :is(table,tr,td,th,hr,fieldset,.wc-block-components-panel) {
  border-color: #e2ded6;
}

/* Les mentions secondaires gardent leur hiérarchie — assombries, pas effacées.
   #6b6862 sur #fbfaf8 : 5,4:1, au-dessus du seuil AA. */
body.woocommerce-checkout .wc-block-components-totals-item__label,
body.woocommerce-checkout .wc-block-components-product-metadata { color: #6b6862; }

/* ⚠️ LES CHAMPS DE SAISIE : LA SPÉCIFICITÉ EST LE SUJET.
   Relevé après le premier essai : les champs restaient NOIRS sur la page claire — texte clair
   sur fond noir au milieu d'un formulaire crème. `assets/css/woocommerce.css` les habille avec
   des sélecteurs plus longs que `.wc-block-checkout input`. On reprend donc la classe que
   WooCommerce pose lui-même sur chaque champ, et on la préfixe de `body`. */
/* ⚠️ MARTEAU DÉLIBÉRÉ. On cible TOUS les champs de saisie sous body.woocommerce-checkout
   et body.woocommerce-order-received, quelle que soit leur classe. Le sélecteur précédent
   listait les classes de WooCommerce Blocks — que WooCommerce a cessé d'appliquer
   systématiquement en 2024. Résultat : l'input « ADRESSE E-MAIL » retombait sur la règle
   globale `.field input` du viseur, fond noir, texte blanc. Yan l'a vu sur sa capture. */
body.woocommerce-checkout :is(input,select,textarea):not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]),
body.woocommerce-order-received :is(input,select,textarea):not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]) {
  background: #ffffff;
  background-color: #ffffff;
  color: #1c1b19;
  border: 1px solid #cfcac1;
}
body.woocommerce-checkout :is(input,select,textarea)::placeholder,
body.woocommerce-order-received :is(input,select,textarea)::placeholder {
  color: #8a8680;
}
body.woocommerce-checkout .wc-block-components-text-input label,
body.woocommerce-checkout .wc-block-components-combobox label { color: #6b6862; }

/* ⚠️ LES OPTIONS DE LIVRAISON ET DE PAIEMENT — MARTEAU DÉLIBÉRÉ.
   Ancien correctif : `.wc-block-components-radio-control-accordion-option` et
   `.wc-block-components-radio-control__option`. Sur la capture de Yan (24.08.2026),
   TWINT (option sélectionnée) apparaissait en clair, mais Visa/Mastercard/PostFinance/
   Klarna (options fermées) restaient sur fond NOIR. WooCommerce Blocks pose une classe
   différente sur les options fermées de l'accordéon — peut-être `--collapsed`, peut-être
   pas — et notre sélecteur ne les touchait pas.

   On cible désormais TOUT ce qui a `radio-control`, `payment-method` ou `shipping-rate`
   dans son nom, quelle que soit la déclinaison. Un attribut au sélecteur suffit à passer
   devant la règle globale du viseur, qui pose sur `main` un fond sombre hérité par les
   sections. */
body.woocommerce-checkout [class*="wc-block-components-radio-control"],
body.woocommerce-checkout [class*="wc-block-components-payment-method"],
body.woocommerce-checkout [class*="wc-block-components-shipping-rates"],
body.woocommerce-checkout [class*="wc-block-checkout__payment"],
body.woocommerce-checkout [class*="wc-block-checkout__shipping"],
body.woocommerce-order-received [class*="wc-block-components-radio-control"],
body.woocommerce-order-received [class*="wc-block-components-payment-method"] {
  background: #ffffff;
  background-color: #ffffff;
  color: #1c1b19;
  border-color: #e2ded6;
}

/* ⚠️ Le bouton d'action NE BASCULE PAS : ambre plein, texte sombre, comme partout ailleurs.
   C'est le seul repère de marque de cet écran — le retourner le rendrait anonyme. */
body.woocommerce-checkout .wc-block-components-button:not(.is-link) {
  background: var(--ambre);
  color: #1c1b19;
  border-color: var(--ambre);
}

/* Les liens : l'ambre sur crème tombe à 1,9:1 — illisible. On garde la couleur de la maison en
   la fonçant juste assez (#8a6524 sur #fbfaf8 : 5,9:1), et on garde le soulignement. */
body.woocommerce-checkout .wc-block-checkout a:not(.wc-block-components-button) {
  color: #8a6524;
  text-decoration: underline;
}

body.woocommerce-checkout .wc-block-components-quantity-selector {
  background: #ffffff;
  border: 1px solid #cfcac1;
}
body.woocommerce-checkout .wc-block-components-quantity-selector__button {
  background: transparent;
  color: #1c1b19;
  border: 0;
}

/* ⚠️ LA PASTILLE DE QUANTITÉ, dans le résumé. Sur les captures de Yan, cercle noir
   à chiffre blanc — passait pour un défaut de style et non un compteur. Elle prend
   la couleur de marque, comme le bouton principal du tunnel. */
body.woocommerce-checkout .wc-block-components-order-summary-item__quantity,
body.woocommerce-order-received .wc-block-components-order-summary-item__quantity,
body.woocommerce-checkout [class*="__quantity"],
body.woocommerce-order-received [class*="__quantity"] {
  background: var(--ambre);
  color: #1c1b19;
  border: 1px solid var(--ambre);
}

body.woocommerce-checkout .wc-block-components-totals-coupon .wc-block-components-panel__button {
  color: #8a6524;
  border-color: transparent;
}
body.woocommerce-checkout .wc-block-components-totals-coupon .wc-block-components-panel__button:hover {
  text-decoration: underline;
}

/* ---- « Ajouter un code promo » ne touche plus le filet ------------------------------------
   Relevé au navigateur : `padding: 14.4px 19.2px 14.4px 0px` — le rembourrage GAUCHE est nul.
   Le « A » démarrait donc exactement sur le bord du conteneur. Ce n'est pas une question de
   goût : un texte qui touche son filet se lit comme s'il en débordait. Vaut sur les deux
   écrans, clair comme sombre. */
.wc-block-components-totals-coupon .wc-block-components-panel__button,
.wc-block-components-totals-coupon .wc-block-components-panel__content {
  padding-left: clamp(0.9rem, 2.4vw, 1.2rem);
  padding-right: clamp(0.9rem, 2.4vw, 1.2rem);
}

/* ---- La page de remerciement suit la commande ---------------------------------------------
   Demande de Yan, 23.08.2026 : « la page merci doit aussi être en clair ».

   Elle est la dernière marche du même escalier : on y relit un numéro de commande, un montant,
   deux adresses. C'est encore de la lecture de contrôle, pas de la contemplation.

   ⚠️ Elle n'est PAS rendue par le bloc `wc-block-checkout` mais par le gabarit classique de
   WooCommerce — d'où des sélecteurs entièrement différents. La classe de corps
   `woocommerce-order-received` est le seul point commun, et c'est par elle qu'on entre. */
body.woocommerce-order-received .page { color: #1c1b19; }

body.woocommerce-order-received .woocommerce {
  background: #fbfaf8;
  color: #1c1b19;
  border: 1px solid #e2ded6;
  border-radius: 3px;
  padding: clamp(1.2rem, 3vw, 2.2rem);
}

body.woocommerce-order-received .woocommerce :is(h1,h2,h3,p,span,td,th,li,strong,em,address,dt,dd) {
  color: #1c1b19;
}

body.woocommerce-order-received .woocommerce :is(table,tr,td,th,ul,li,hr) {
  border-color: #e2ded6;
}

/* Le bandeau du récapitulatif : une surface un ton plus soutenu, pour qu'il se détache. */
body.woocommerce-order-received .woocommerce-order-overview {
  background: #f2efe9;
  border: 1px solid #e2ded6;
  border-radius: 2px;
}

/* Les libellés en capitales espacées gardent leur rôle de second plan — 5,4:1 sur crème. */
body.woocommerce-order-received .woocommerce-order-overview li strong { color: #1c1b19; }
body.woocommerce-order-received .woocommerce :is(th, .woocommerce-order-overview li) { color: #6b6862; }

body.woocommerce-order-received .woocommerce a:not(.button) {
  color: #8a6524;
  text-decoration: underline;
}
body.woocommerce-order-received .woocommerce .button {
  background: var(--ambre);
  color: #1c1b19;
  border-color: var(--ambre);
}

/* ⚠️ Le récapitulatif de commande restait EN BLANC sur la surface claire — signalé par Yan :
   « numéro de commande, date et moyen sont en blanc et pas visibles ». Seul le montant se
   voyait, parce qu'il porte sa propre classe.

   `woocommerce.css` colore ces valeurs avec des sélecteurs plus longs que les nôtres. On reprend
   donc chaque porteur de valeur nommément — `strong`, `mark`, `.woocommerce-Price-amount` — au
   lieu d'espérer qu'une règle générale les attrape. C'est la deuxième fois ce soir qu'une
   bascule de couleurs laisse un élément derrière : la leçon est qu'on les relève au navigateur,
   un par un, jamais à la lecture. */
body.woocommerce-order-received .woocommerce-order-overview :is(li, strong, mark, span),
body.woocommerce-order-received .woocommerce-table :is(td, th, strong, mark, span, bdi),
body.woocommerce-order-received .woocommerce :is(mark, bdi) {
  color: #1c1b19;
  background: transparent;
}
body.woocommerce-order-received .woocommerce-order-overview li {
  color: #6b6862;   /* le libellé reste au second plan… */
}
body.woocommerce-order-received .woocommerce-order-overview li strong {
  color: #1c1b19;   /* …la valeur, au premier */
}

/* ---- Le lien d'évitement et le pied de page ----------------------------------------------
   Posés le 24.08.2026 sur relevé d'audit : il n'y avait ni l'un ni l'autre.

   ⚠️ Le lien d'évitement est **caché à la souris, jamais au clavier**. `display:none` le
   retirerait aussi de l'ordre de tabulation — il ne servirait plus à rien. On le sort de l'écran
   et on le ramène au focus, la seule technique qui laisse la fonction intacte. */
.evitement {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ambre); color: var(--noir);
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.12em; text-transform: uppercase;
  padding: 0.8rem 1.2rem; text-decoration: none;
}
.evitement:focus { left: 0; }

/* Le pied : discret, mais **réel**. Il double le chemin du viseur — pour les robots, pour les
   claviers, et pour le jour où le viseur ne se charge pas. Cette nuit a montré que ça arrive. */
.pied {
  border-top: 1px solid var(--line);
  padding: clamp(1.6rem, 4vw, 2.6rem) clamp(1rem, 4vw, 2rem);
  background: var(--noir);
}
.pied__nav ul {
  list-style: none; margin: 0 0 1rem; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.4rem;
}
.pied__nav a {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.1em;
  color: var(--gris-2); text-decoration: none;
}
.pied__nav a:hover, .pied__nav a:focus-visible { color: var(--ambre); text-decoration: underline; }
.pied__mention {
  font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.08em;
  color: var(--gris); margin: 0;
}

/* ⚠️ Sur le tunnel passé en clair, le pied garde le noir du site : il n'appartient pas au
   formulaire, il appartient à la page. */
body.woocommerce-checkout .pied, body.woocommerce-order-received .pied { background: var(--noir); }
body.woocommerce-checkout .pied :is(a, p), body.woocommerce-order-received .pied :is(a, p) {
  color: var(--gris-2);
}

/* ⚠️ Un titre SORTI DE L'ŒIL, pas du document — 24.08.2026.
   `display:none` le retirerait aussi de l'arbre d'accessibilité : on aurait un titre que
   personne ne lit, ce qui ne vaut pas mieux que pas de titre. Le clip le laisse audible.

   ── `.hors-ecran` : le même clip, pour un BLOC et non plus une ligne — 23.08.2026 ──────────
   Le plan du viseur s'en sert (voir `kimb_plan_du_viseur()` dans `functions.php`). Deux écarts
   avec la version « titre », et chacun a une raison :

     · `white-space:normal` — `nowrap` sur une liste de douze titres produirait une ligne unique
       de plusieurs milliers de pixels. Elle est clipée, donc invisible, mais elle reste dans le
       flux de calcul et certains moteurs de rendu la font peser.
     · `height:1px` tient quand même : le clip ne dépend pas de la hauteur réelle du contenu. */
.titre-hors-ecran,
.hors-ecran {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; margin: -1px;
}
.hors-ecran { white-space: normal; }


/* ---- 🔴 LE PIED DE PAGE ÉCRASAIT LE VISEUR — corrigé le 24.08.2026 ------------------------
   Signalé par Yan, capture à l'appui : sur téléphone, la photographie n'occupait plus que le
   tiers haut de l'écran, et le cartel, les boutons et le pied s'empilaient dessous sur du noir.

   La cause est de moi, et elle date de vingt minutes plus tôt. `body` est une colonne flex ;
   `.viewfinder` y prend `flex:1 1 auto` avec `min-height:80vh` — mais une règle de media query
   remet ce minimum à **0** sur les écrans courts. Tant que le viseur était seul dans la colonne,
   il prenait tout. Le pied ajouté a réclamé sa part, le minimum ne le retenait plus, et le
   viseur s'est effondré à la hauteur de son contenu — c'est-à-dire presque rien, ses enfants
   étant en `position:absolute`.

   ⚠️ C'est le MÊME défaut que la fiche d'œuvre, trois heures plus tôt, et je ne l'ai pas vu
   venir en ajoutant le pied. Un élément qui tire sa hauteur d'un partage flex la perd dès qu'un
   voisin arrive. La règle : **ce qui doit occuper l'écran ne négocie pas sa hauteur.**

   Le viseur prend donc le premier écran, et le pied vit dessous — accessible en faisant défiler,
   ce qui est exactement sa place : un plan du site n'a pas à couvrir la photographie. */
body.home .viewfinder,
body.single-product .viewfinder {
  flex: 0 0 auto;
  height: 100vh;
  height: 100dvh;
  min-height: 100vh;
  min-height: 100dvh;
}

/* ⚠️ Sur une fiche, la fiche vient juste après : le viseur cède un peu de hauteur pour qu'on
   devine qu'il y a quelque chose dessous. Sur l'accueil, rien ne suit que le pied — il prend
   tout. */
body.single-product .viewfinder { height: 88dvh; min-height: 88dvh; }

/* La grappe de contrôles est posée en absolu DANS le viseur : elle suit sa hauteur, et ne
   déborde plus sur ce qui vient après. */
body.home .pied, body.single-product .pied { position: relative; z-index: 1; }

/* ---- Sur téléphone, le décor cède la place au choix -----------------------------------------
   Signalé par Yan, capture à l'appui : « l'image prend un peu beaucoup de place en hauteur, 2/3
   c'est du décor et on ne voit pas assez directement les formats ».

   Mesuré : `.preview__scene.is-wall` est en **205/230**, presque carré — soit ~420 px de scène
   sur un écran de 375 de large. Le tirage lui-même n'en occupe qu'une fraction ; le reste est du
   mur, une commode et une plinthe. Sur un grand écran cette mise en scène a un sens : elle donne
   l'échelle. Sur un téléphone, elle repousse les formats et le prix sous la ligne de flottaison,
   et c'est précisément ce qu'on est venu choisir.

   ⚠️ **On raccourcit la scène, on ne réduit pas le tirage.** L'échelle du cadre est calculée sur
   la LARGEUR du mur (`WALL_CM = 205` dans `viseur.js`) : rogner en hauteur ne touche donc pas au
   rapport entre le tirage et la commode — l'argument de la vue reste intact. C'est le vide
   au-dessus et en dessous qui s'en va.

   ⚠️ Le cadre est ancré à `bottom: 64%`. En raccourcissant la scène, il fallait le descendre :
   au format le plus grand il serait sorti par le haut. Vérifié au navigateur sur 60 × 90, le cas
   limite. */
@media (max-width: 720px) {
  .preview__scene.is-wall {
    aspect-ratio: 205 / 172;
  }

  /* Le tirage descend avec la scène : il garde sa marge au-dessus de la commode, il cesse de
     flotter au milieu d'un mur vide.

     ⚠️ Mesuré au navigateur, au plus grand format : à `bottom:56%` le cadre arrivait à **2 px**
     du haut de la scène. Pas de débordement, mais un tirage collé au plafond ne donne plus
     l'échelle — c'est pourtant tout ce qu'on demande à cette vue. À 50 %, il reste 17 px en haut
     et 10 px au-dessus de la commode. */
  .preview__scene.is-wall .frameview { bottom: 50%; }

  /* ⚠️ Garde-fou de dernier recours : un format qu'on ajouterait demain, plus grand que tous
     ceux d'aujourd'hui, ne doit pas pouvoir sortir de la scène. La hauteur est bornée, jamais la
     largeur — c'est la largeur qui porte l'échelle. */
  .preview__scene.is-wall .frameview { max-height: 62%; }
  .preview__scene.is-wall .frameview__img { max-height: 100%; }

  /* La commode occupait un tiers de la hauteur pour ne rien dire du tirage. */
  .preview__scene.is-wall .commode { height: 27%; }
  .preview__scene.is-wall .mur__plinthe,
  .preview__scene.is-wall .commode { bottom: 19%; }

  /* Le reste du panneau se resserre : chaque rem gagné ici est une ligne de format visible. */
  .preview__scene { padding: 1rem; min-height: 200px; }
  .config { padding: 1rem; gap: 0.9rem; }
  .preview__cap { margin: 0.5rem 0; }
}

/* Le menu servi par `gneiz-image-protection` porte son propre habillage, volontairement
   sobre : il doit tenir sur n'importe quel site. Il l'expose en variables `--gneiz-menu-*`
   pour qu'un thème lui donne son vocabulaire sans lutter contre un style en ligne — et
   c'est ce qu'on fait ici. Sans ces sept lignes, le menu de Kim B Lucas s'ouvrirait en
   police système au milieu d'une galerie en Caslon. */
:root {
  --gneiz-menu-fond: rgba(8,8,7,0.92);
  --gneiz-menu-bord: var(--line);
  --gneiz-menu-encre: var(--blanc);
  --gneiz-menu-encre-douce: var(--gris-2);
  --gneiz-menu-encre-faible: var(--gris);
  --gneiz-menu-survol: rgba(255,255,255,0.05);
  --gneiz-menu-police: var(--sans);
  --gneiz-menu-police-titre: var(--serif);
}

/* ══════════════════════════════════════════════════ le menu de l'œuvre ══════
   Ces règles-ci habillent la version AUTONOME, celle qui ne se pose que sans le plugin —
   le prototype, ou un site dont l'extension serait éteinte. En service normal, c'est le
   menu du plugin qui s'ouvre, habillé par les variables ci-dessus.

   Le clic droit sur la scène ouvre un cartel au lieu du menu du navigateur.
   Ce n'est pas un verrou — l'image est déjà chez le visiteur — c'est une
   réponse : qui cherchait « Enregistrer sous » trouve le titre, l'auteur, et
   un chemin vers l'achat. Demandé le 29.08.2026.

   Même matière que le reste du viseur : fond noir translucide, flou, filet
   d'un pixel. Rien de neuf, sinon il aurait l'air d'appartenir au navigateur
   et pas à la galerie. */
.oeuvremenu {
  position:fixed; z-index:60; min-width:15rem; max-width:19rem;
  padding:0.85rem 0; border:1px solid var(--line); border-radius:3px;
  background:rgba(8,8,7,0.92); backdrop-filter:blur(10px);
  box-shadow:0 12px 40px rgba(0,0,0,0.55);
  animation:fade 0.18s var(--ease);
}
.oeuvremenu[hidden] { display:none; }
.oeuvremenu__t { margin:0; padding:0 1.1rem; font-family:var(--serif);
                 font-size:0.98rem; line-height:1.3; color:var(--blanc); }
/* 0.75rem et non 0.68 : le plancher de lisibilité du viseur (#119) est à 12 px, et 0.68rem
   n'en valait que 10,9. Les deux libellés sont nés avec le menu du clic droit (#586), qui ne
   connaissait pas la règle. */
.oeuvremenu__s { margin:0.2rem 0 0; padding:0 1.1rem; font-family:var(--mono);
                 font-size:0.75rem; letter-spacing:0.06em; text-transform:uppercase;
                 color:var(--gris); }
.oeuvremenu__c { margin:0.5rem 0 0; padding:0 1.1rem; font-family:var(--mono);
                 font-size:0.75rem; color:var(--gris); }
.oeuvremenu__sep { height:1px; margin:0.75rem 0; background:var(--line); }

/* Les entrées sont tantôt un lien, tantôt un bouton — selon qu'il y a une
   fiche ou non. Elles doivent se ressembler à l'œil, d'où le style commun et
   la remise à zéro de l'apparence native du bouton. */
.oeuvremenu__a {
  display:block; width:100%; box-sizing:border-box; padding:0.5rem 1.1rem;
  font-family:var(--sans); font-size:0.85rem; text-align:left; text-decoration:none;
  color:var(--gris-2); background:none; border:0; cursor:pointer;
  transition:color 0.15s var(--ease), background 0.15s var(--ease);
}
.oeuvremenu__a:hover, .oeuvremenu__a:focus-visible {
  color:var(--blanc); background:rgba(255,255,255,0.05);
}

/* Au doigt, il n'y a pas de clic droit : l'appui long déclenche le menu du
   système, qu'on ne peut pas remplacer. Le menu resterait invisible et
   n'encombre donc pas la feuille tactile. */
@media (hover:none) { .oeuvremenu { display:none; } }

/* ---- LE VISEUR OCCUPE L'ÉCRAN SUR UNE FICHE D'ŒUVRE — 30.08.2026 ----------------------------
   Décision de Yan, qui renverse celle du 23.08 : voir le commentaire de `functions.php`, qui
   porte le motif et le risque assumé.

   `kimb-viseur-plein` est posée par WordPress sur le `<body>` des seules fiches d'œuvre. Le
   prototype ne la porte jamais — cette règle y est donc inerte, et c'est voulu : le prototype
   n'a pas de fiche WooCommerce sous son viseur.

   On borne la FENÊTRE, pas le contenu : la fiche et le pied restent entiers dans le document,
   lisibles sans JavaScript et par un lecteur d'écran qui parcourt les repères. */
body.kimb-viseur-plein { height:100vh; height:100dvh; overflow:hidden; }

/* Borner le `body` ne suffisait pas, et la première tentative l'a montré : `.viewfinder` est
   `flex:1 1 auto` avec un plancher à 80vh, et la fiche produit reste un frère de flex sous lui.
   Les deux se partageaient donc la hauteur — la photographie s'arrêtait aux quatre cinquièmes
   de l'écran et le dernier cinquième montrait le haut de la fiche, en noir. On donne au viseur
   la fenêtre entière ; la fiche et le pied restent dans le document, sous la ligne de flottaison. */
body.kimb-viseur-plein .viewfinder { flex:0 0 auto; height:100vh; height:100dvh; min-height:0; }
