/* qc-photo.css — styles du widget photo (galerie, vignettes, visionneuse,
   viseur plein écran), partagés entre /report/<code> (page complète) et le
   pipeline mobile usine (31/08) : factorisé pour ne pas dupliquer ~150
   lignes entre les deux templates (règle DRY). Ce que chaque page garde de
   SON côté : le déclencheur .qcphoto-trigger (taille/position différentes
   selon la page) et l'entête (.head sur report.html, mise en page propre au
   pipeline sur pipeline.html).

   Portage du patron déjà validé côté WMS pour le viseur (components/
   PhotoCapture.tsx + styles.css .photo-cam-*) : overlay noir fixed, bouton
   rond avec anneau de chargement tournant, croix de fermeture loin du
   déclencheur (le bouton lui-même n'est jamais bloqué : on peut enchaîner
   les prises pendant que les précédentes montent). */

.qcphoto-status { font-size:13px; color:var(--muted); margin-top:8px; min-height:18px; }
.qcphoto-gallery { display:grid; grid-template-columns:repeat(auto-fill, minmax(96px, 1fr)); gap:10px; margin-top:12px; }
.qcphoto-thumb { position:relative; border:1px solid var(--border); border-radius:8px; overflow:hidden; aspect-ratio:1; }
.qcphoto-thumb img { width:100%; height:100%; object-fit:cover; display:block; cursor:zoom-in; }
.qcphoto-thumb.qcphoto-uploading img { opacity:.5; }
/* Épais + clignotant (01/09, demande explicite Nicolas : "grosse ligne
   rouge épaisse... fais-la clignoter") -- une photo échouée (3 tentatives
   automatiques épuisées, cf. uploadBlob() côté JS) doit sauter aux yeux
   dans une galerie de vignettes, pas se fondre dans le reste. outline (pas
   border) : ne pousse pas la mise en page, se dessine par-dessus. */
.qcphoto-thumb.qcphoto-failed { outline:4px solid var(--fail); animation:qcphoto-failed-blink 1s step-start infinite; }
@keyframes qcphoto-failed-blink { 50% { outline-color:transparent; } }
/* Anneau de pourcentage AU MILIEU de la vignette pendant l'envoi (01/09,
   demande Nicolas : voir en temps réel où en est chaque photo pendant
   qu'elle bascule du téléphone vers le serveur). r=15.9155 sur un viewBox
   36x36 -> circonférence exactement 100, donc stroke-dasharray prend le
   pourcentage tel quel (astuce standard des anneaux de progression SVG,
   pas besoin de calculer 2*pi*r en JS). Toujours dans le DOM (même vignette
   pour les photos déjà là), affiché seulement pendant .qcphoto-uploading. */
.qcphoto-thumb-progress { position:absolute; inset:0; display:none; place-items:center;
                            background:rgba(0,0,0,.35); }
.qcphoto-thumb.qcphoto-uploading .qcphoto-thumb-progress { display:grid; }
.qcphoto-thumb-progress > * { grid-area:1/1; }
.qcphoto-ring { width:70%; max-width:44px; transform:rotate(-90deg); }
.qcphoto-ring-bg { fill:none; stroke:rgba(255,255,255,.35); stroke-width:3; }
.qcphoto-ring-fg { fill:none; stroke:#fff; stroke-width:3; stroke-linecap:round;
                    stroke-dasharray:0 100; transition:stroke-dasharray .12s linear; }
.qcphoto-ring-pct { justify-self:center; align-self:center; color:#fff; font-size:11px;
                     font-weight:700; text-shadow:0 1px 2px rgba(0,0,0,.6); }
.qcphoto-del { position:absolute; top:4px; right:4px; width:26px; height:26px;
               border-radius:999px; border:none; background:rgba(0,0,0,.55); color:#fff;
               cursor:pointer; padding:0; display:grid; place-items:center; }
.qcphoto-del svg { width:14px; height:14px; }
.qcphoto-del:hover { background:var(--fail); }
/* Sauvegarde locale (01/09) -- coin bas-droit, sous la suppression : partage
   natif si dispo, sinon téléchargement classique (cf. saveImageLocally()). */
.qcphoto-save { position:absolute; bottom:4px; right:4px; width:26px; height:26px;
                 border-radius:999px; border:none; background:rgba(0,0,0,.55); color:#fff;
                 cursor:pointer; padding:0; display:grid; place-items:center; }
.qcphoto-save svg { width:14px; height:14px; }
.qcphoto-save:hover { background:var(--accent); }
/* Retenter manuellement (01/09) -- coin bas-gauche, visible SEULEMENT sur
   une vignette .qcphoto-failed (les 3 tentatives automatiques ont toutes
   échoué, cf. uploadBlob() côté JS) -- inutile de l'afficher sur une photo
   déjà envoyée avec succès. */
.qcphoto-retry { position:absolute; bottom:4px; left:4px; width:26px; height:26px;
                  border-radius:999px; border:none; background:rgba(0,0,0,.55); color:#fff;
                  cursor:pointer; padding:0; display:none; place-items:center; }
.qcphoto-thumb.qcphoto-failed .qcphoto-retry { display:grid; }
.qcphoto-retry svg { width:14px; height:14px; }
.qcphoto-retry:hover { background:var(--accent); }
/* Sélection multiple (31/08, /report/<code> seulement — pas chargé sur le
   pipeline usine, pas de qc-multiselect.js là-bas) : pastille en haut à
   gauche, "Ctrl+clic permanent" indispensable au tactile. */
.qcphoto-thumb.selected { box-shadow: inset 0 0 0 3px #3b82f6; }
.qcphoto-pick { position:absolute; top:4px; left:4px; width:22px; height:22px;
                border-radius:999px; border:2px solid rgba(255,255,255,.85);
                background:rgba(0,0,0,.35); color:transparent; cursor:pointer;
                display:grid; place-items:center; }
.qcphoto-pick svg { width:13px; height:13px; }
.qcphoto-thumb.selected .qcphoto-pick { background:#3b82f6; border-color:#3b82f6; color:#fff; }

/* Voir en grand — ouvert à tous (lecture), contrairement au viseur de
   capture ci-dessous. Même vignette (pas de variante haute résolution
   séparée), juste affichée en entier. Flèches COLLÉES à la photo (retour
   Nicolas : pas aux extrémités de l'écran) : .qcphoto-lightbox-stage en
   inline-block se RÉDUIT à la taille réelle de l'image (jamais étiré à toute
   la largeur du viewport) -- les flèches, en position:absolute dedans,
   collent donc exactement au bord de la photo. */
.qcphoto-lightbox { position:fixed; inset:0; height:100dvh; z-index:1000;
                     display:grid; place-items:center; background:rgba(0,0,0,.92); }
.qcphoto-lightbox[hidden] { display:none; }
.qcphoto-lightbox-stage { position:relative; display:inline-block;
                            max-width:calc(100vw - 140px); max-height:88dvh; }
.qcphoto-lightbox-stage img { display:block; max-width:100%; max-height:88dvh; object-fit:contain; }
.qcphoto-lightbox-close { position:absolute; top:max(env(safe-area-inset-top,0px),10px); right:12px;
                            width:44px; height:44px; border:0; border-radius:50%; background:rgba(0,0,0,.45);
                            color:#fff; display:grid; place-items:center; cursor:pointer; }
.qcphoto-lightbox-close svg { width:20px; height:20px; }
/* Précédente/suivante, rotation infinie : masquées d'elles-mêmes s'il n'y a
   qu'une seule photo (cf. JS) -- pas de flèche qui ne mène nulle part. */
.qcphoto-lightbox-nav { position:absolute; top:50%; transform:translateY(-50%);
                          width:48px; height:48px; border:0; border-radius:50%;
                          background:rgba(0,0,0,.45); color:#fff; display:grid;
                          place-items:center; cursor:pointer; }
.qcphoto-lightbox-nav[hidden] { display:none; }
.qcphoto-lightbox-nav:hover { background:rgba(0,0,0,.65); }
.qcphoto-lightbox-nav svg { width:22px; height:22px; }
.qcphoto-lightbox-prev { right:100%; margin-right:16px; }
.qcphoto-lightbox-next { left:100%; margin-left:16px; }
@media (max-width:560px) {
  .qcphoto-lightbox-stage { max-width:calc(100vw - 100px); }
  .qcphoto-lightbox-prev { margin-right:8px; }
  .qcphoto-lightbox-next { margin-left:8px; }
  .qcphoto-lightbox-nav { width:38px; height:38px; }
  .qcphoto-lightbox-nav svg { width:18px; height:18px; }
}

/* Viseur plein écran */
.qcphoto-overlay { position:fixed; inset:0; height:100dvh; z-index:1000; display:flex;
                    flex-direction:column; justify-content:flex-end; background:#000;
                    overscroll-behavior:contain; touch-action:manipulation; }
.qcphoto-overlay[hidden] { display:none; }
.qcphoto-stage { position:absolute; inset:0; display:grid; place-items:center; overflow:hidden; }
/* cover (31/08, retour Nicolas : "plein écran") -- contain laissait des
   bandes noires haut/bas sur un écran de téléphone (aspect vidéo 4:3 très
   différent d'un écran 9:19.5+) ; cover recadre pour remplir tout l'écran,
   comme n'importe quel appareil photo natif. La photo capturée (capture()
   dans qc-photo.js) part toujours du flux vidéo brut, pas du cadrage visuel
   -- aucune perte de résolution, juste l'AFFICHAGE qui change. */
.qcphoto-stagevideo { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; background:#000; }
.qcphoto-opening { position:absolute; color:rgba(255,255,255,.75); font-size:14px; }
.qcphoto-flash { position:absolute; inset:0; background:#fff; opacity:0; pointer-events:none;
                  transition:opacity 90ms ease-out; }
.qcphoto-flash.is-on { opacity:.35; transition-duration:30ms; }
.qcphoto-close { position:absolute; top:max(env(safe-area-inset-top,0px),10px); right:12px;
                  width:44px; height:44px; border:0; border-radius:50%; background:rgba(0,0,0,.45);
                  color:#fff; display:grid; place-items:center; cursor:pointer; }
.qcphoto-close svg { width:20px; height:20px; }
/* Consigne du protocole guidé À L'INTÉRIEUR du viseur (31/08, retour
   Nicolas : le viseur plein écran cache tout ce qu'il y a derrière, donc la
   consigne affichée sur la page ne servait à rien une fois la caméra
   ouverte -- il fallait la même info DANS l'overlay). Posée dans .qcphoto-bar,
   juste AU-DESSUS du déclencheur (31/08, 2e retour : d'abord en pastille
   coin haut-gauche, déplacée ici -- l'œil est déjà en bas de l'écran au
   moment de photographier, pas la peine de le faire remonter). Simple
   enfant flex de .qcphoto-bar (column), pas de position absolue. */
.qcphoto-overlay-label { padding:8px 16px; border-radius:999px; background:rgba(0,0,0,.55);
                           color:#fff; font-size:14px; font-weight:600; text-align:center; }
.qcphoto-overlay-label[hidden] { display:none; }
.qcphoto-bar { position:relative; z-index:1; flex:0 0 auto; display:flex; flex-direction:column;
               align-items:center; gap:12px; padding:16px 16px max(env(safe-area-inset-bottom,0px),16px);
               background:linear-gradient(to top, rgba(0,0,0,.55), rgba(0,0,0,0)); }
/* Choisir depuis l'album / fichiers (01/09, demande Nicolas : aucun moyen
   d'ajouter une photo déjà prise ou un fichier, seulement la caméra live).
   Rangée à 3 colonnes égales pour garder le déclencheur bien CENTRÉ, que le
   bouton album soit là ou non (repli natif appareil photo : icône à gauche
   du déclencheur, comme iOS/Android). */
.qcphoto-shutter-row { display:grid; grid-template-columns:44px 74px 44px; align-items:center;
                         justify-content:center; gap:28px; }
.qcphoto-gallery-pick, .qcphoto-shutter-spacer { width:44px; height:44px; }
.qcphoto-gallery-pick { border:0; border-radius:10px; background:rgba(255,255,255,.16);
                          color:#fff; display:grid; place-items:center; cursor:pointer; }
.qcphoto-gallery-pick svg { width:20px; height:20px; }
.qcphoto-gallery-pick:active { background:rgba(255,255,255,.32); }
.qcphoto-shutter { position:relative; width:74px; height:74px; border-radius:50%;
                    border:4px solid rgba(255,255,255,.9); background:rgba(255,255,255,.22);
                    color:#fff; display:grid; place-items:center; cursor:pointer;
                    transition:transform .08s ease, background .15s ease; }
.qcphoto-shutter svg { width:26px; height:26px; }
.qcphoto-shutter:active { transform:scale(.92); background:rgba(255,255,255,.4); }
.qcphoto-shutter:disabled { opacity:.5; }
/* Anneau qui tourne pendant l'envoi -- "barre de loading autour de l'icône" :
   on peut enchaîner les prises sans attendre, l'anneau dit juste que le
   serveur n'a pas fini d'encaisser les précédentes. */
.qcphoto-shutter::after { content:""; position:absolute; inset:-9px; border-radius:50%;
                           border:3px solid transparent; border-top-color:var(--accent);
                           border-right-color:var(--accent); opacity:0;
                           transition:opacity .15s ease; pointer-events:none; }
.qcphoto-shutter-busy::after { opacity:1; animation:qcphoto-snake .9s linear infinite; }
@keyframes qcphoto-snake { to { transform:rotate(1turn); } }
@media (prefers-reduced-motion: reduce) {
  .qcphoto-shutter-busy::after { animation:none; }
  .qcphoto-flash { display:none; }
  .qcphoto-thumb.qcphoto-failed { animation:none; } /* garde l'outline épais fixe, juste sans le clignotement */
}
.qcphoto-done { border:0; background:rgba(255,255,255,.16); color:#fff; font-size:15px;
                 min-height:44px; padding:10px 22px; border-radius:999px; cursor:pointer; }
