/* tools/atelier/app.css — Portail Atelier : règles PROPRES à la tablette. La coquille pk-* commune aux deux
   portails vit dans tools/portal-shell.css (ge-109, 2026-09-13), chargée par atelier.php AVANT ce fichier :
   à spécificité égale, une règle d'ici surcharge donc une règle du shell. Toute règle commune va dans le shell ;
   garde-fou : node scripts/css_shared_diff.js (0 règle dupliquée).
   Viewport MESURÉ le 2026-09-11 (ge-120, sonde sur l'appareil) : portrait 600×936, paysage 960×576, DPR 2 —
   bascule sidebar ≥ 900px (le parc bascule à 1024 : les blocs @media desktop restent propres à chaque portail).
   Échelle z-index du portail : .topbar 50 · .pk-tabbar 150 · #or-fiche/#tri-fiche/#veh-fiche/#ctl-fiche (fiches plein écran) 200 ·
   .pk-dim 201 / .pk-sheet 202 (au-dessus des fiches) · visionneuse photo 260 · .toast/.at-toasts 500.
   Écarts voulus vs parc, tous ici : .scan-torch et .pdf-zoombtns button à 48 px (cible gantée), .pk-row min-height
   48 px, .pk-dim/.pk-sheet (z-index, débordement, visible ≥ 900px — c'est le seul dialogue du portail), #pk-minimap isolé.
   Les six blocs copiés du parc et jamais utilisés ici (.pk-veh-list, .pk-acc-cols, .dates-grid, #ech-container,
   .seg-btn, .empty-state) ont été supprimés à ge-109 ; le .pk-day{margin-bottom:0} desktop du parc n'a pas été
   repris : dans ce fichier la base .pk-day venait après et l'annulait (inerte depuis le lot 2) — retiré plutôt
   qu'activé, rendu identique exigé. */
/* 48px, pas 40 : bouton tactile sur le chemin caméra d'une tablette manipulée avec des gants
   (règle des 48 px du plan ; la valeur 40 était héritée du scanner parc) — revue finale lot 2, M4. */
.scan-torch{position:absolute;top:10px;right:10px;width:48px;height:48px;border-radius:50%;border:none;background:rgba(255,255,255,.22);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer;backdrop-filter:blur(4px)}
.pdf-zoombtns button{width:48px;height:48px;border-radius:50%;border:none;background:rgba(255,255,255,.92);color:var(--text);font-size:1.25rem;font-weight:600;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,.3);font-family:inherit}
/* Visionneuse de documents du portail (#at-doc-viewer, lot 3). Parc dimensionne son « × » à 32px :
   sous la cible tactile de 48px imposée au portail atelier. On le remonte, scopé à la visionneuse :
   la base .modal-close (32 px) est commune aux deux portails (portal-shell.css).
   Les boutons de zoom, eux, sont remontés à 48px SUR LA RÈGLE DE BASE ci-dessus : ils servent
   aussi à la visionneuse PHOTO (or.js), qui n'est pas dans #at-doc-viewer — un override scopé
   l'aurait ratée (mesuré à 42px en portrait, 2026-09-12). */
/* Pages d'un PDF rendu par PDF.js : empilées, zoomables comme une image.
   ⚠️ La boîte centre ses enfants (align-items:center). Sur un contenu PLUS GRAND qu'elle, un
   enfant centré déborde des DEUX côtés et le haut devient inatteignable : on ouvrait un PDF
   multi-pages au milieu du document, sans pouvoir remonter (mesuré 2026-09-12). On aligne donc
   en haut dès qu'il y a des pages. `:has()` : Chrome ≥ 105, la tablette est en 152. */
.pdf-imgbox:has(.at-doc-pages){align-items:flex-start}
.at-doc-pages{transform-origin:center top;will-change:transform;padding:8px 0}
.at-doc-pages canvas{box-shadow:0 2px 12px rgba(0,0,0,.35)}
#at-doc-viewer .modal-close{width:48px;height:48px;border-radius:12px;font-size:1.35rem;display:flex;align-items:center;justify-content:center}
/* input date mobile : appearance:none (voir le commentaire iOS dans portal-shell.css) — seuil atelier */
@media (max-width:899px){
  input[type=date]{-webkit-appearance:none;appearance:none;display:block;width:100%;min-height:41px}
}

/* ══ Coquille pk-* — écarts PROPRES à l'atelier (la coquille commune est dans portal-shell.css) ══ */
/* sheet scan (au-dessus des fiches plein écran #or-fiche/#tri-fiche/#veh-fiche et .modal-overlay à 200, sous la visionneuse photo 260 et les toasts 500) */
.pk-dim{position:fixed;inset:0;background:rgba(16,20,28,.4);backdrop-filter:blur(3px);z-index:201;display:none}
/* max-height + overflow : le sheet du scanner (viseur 4/3) fait 693 px et débordait le viewport
   en paysage — titre coupé en haut, bouton Fermer à 25 px de haut en bas, non scrollable.
   Constat posé quand on croyait le paysage à 960×600 ; il est à 960×576 (mesuré, ge-120),
   donc le débordement était encore pire que décrit ici. La borne reste valable. */
.pk-sheet{position:fixed;left:0;right:0;bottom:0;background:var(--surface);border-radius:22px 22px 0 0;padding:14px 18px calc(24px + env(safe-area-inset-bottom));z-index:202;box-shadow:0 -10px 40px rgba(16,20,28,.25);transform:translateY(105%);transition:transform .22s ease;max-height:calc(100vh - 24px);overflow-y:auto;overscroll-behavior:contain}
/* menu : min-height 48 px (cible gantée) — écart vs parc */
.pk-row{display:flex;align-items:center;gap:12px;padding:13px 14px;border:0;border-bottom:1px solid var(--border);font-size:.9rem;font-weight:600;width:100%;background:none;cursor:pointer;text-align:left;font-family:inherit;color:var(--text);min-height:48px}
/* `isolation` n'est PAS décoratif : Leaflet pose z-index:400 sur ses panneaux, et un conteneur
   position:relative sans z-index ne crée pas de contexte d'empilement — les tuiles remontaient
   alors AU-DESSUS de la barre d'actions de la fiche (mesuré : 93 px de recouvrement, 2026-09-12).
   Isoler confine ces z-index à la carte. */
#pk-minimap{height:150px;background:#e4e9ec;isolation:isolate;border-radius:10px;overflow:hidden}
/* accueil desktop : seuil atelier 900 px (parc : 1024) */
@media (min-width:900px){
  .pk-tiles{grid-template-columns:repeat(4,1fr);gap:14px}
  .pk-tile{border-radius:16px;padding:16px 18px}
  .pk-tile .n{font-size:2.05rem;margin-top:2px}
  #pk-minimap{height:260px}
}
/* iOS : un champ < 16px déclenche l'auto-zoom au focus, et l'app RESTE zoomée — 16px l'empêche */
@media (max-width:899px){
  #app input, #app select, #app textarea, #login-screen input, #chpass-screen input{font-size:16px}
}
/* desktop ≥900 (seuil mesuré ge-120 ; parc : 1024) : sidebar bleue + panneau clair arrondi — .pk-side{display:none} de base : portal-shell.css */
@media (min-width:900px){
  #app.pk-on{display:flex;min-height:100vh;background:linear-gradient(165deg,#25488f,var(--eld-blue) 55%,#172e63)}
  .pk-side{display:flex;width:222px;color:#fff;flex-direction:column;padding:24px 14px 18px;flex:none;position:sticky;top:0;height:100vh}
  .pk-slogo{width:104px;height:auto;margin:0 auto 4px;display:block}
  .pk-tagline{text-align:center;font-family:'DM Sans',sans-serif;font-weight:700;font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.55);margin-bottom:22px}
  .pk-nav-i{display:flex;align-items:center;gap:11px;border-radius:11px;padding:10px 12px;font-size:.85rem;font-weight:600;color:rgba(255,255,255,.72);margin-bottom:2px;background:none;border:none;cursor:pointer;width:100%;text-align:left;font-family:inherit}
  .pk-nav-i svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none}
  .pk-nav-i.on{background:rgba(255,255,255,.14);color:#fff}
  .pk-nav-i .pk-ct{margin-left:auto;background:rgba(255,255,255,.16);color:#fff}
  .pk-nav-i .pk-ct.warn{background:var(--orange)}
  .pk-nav-i .pk-ct.crit{background:var(--red)}
  .pk-me{margin-top:auto;display:flex;align-items:center;gap:10px;padding:14px 8px 0;border-top:1px solid rgba(255,255,255,.14);color:#fff}
  .pk-me .pk-avatar{background:rgba(255,255,255,.16);box-shadow:none;border-radius:50%;width:32px;height:32px;font-size:.72rem}
  .pk-me .a{font-size:.8rem;font-weight:700}
  .pk-me .b{font-size:.68rem;color:rgba(255,255,255,.6)}
  .pk-out{margin-left:auto;background:none;border:none;color:rgba(255,255,255,.55);cursor:pointer;padding:6px}
  .pk-out svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
  .pk-content{flex:1;display:flex;flex-direction:column;margin:14px 14px 14px 4px;background:var(--bg);border-radius:20px;overflow:hidden;min-width:0}
  .pk-content .topbar{display:none}
  /* width:100% obligatoire : .main garde margin:0 auto (base mobile), et en flex column
     un margin transversal auto DÉSACTIVE le stretch → sans largeur, chaque section
     tombait en shrink-to-fit (carte réduite à 2px, terrain 2026-09-05) */
  .pk-content .main{flex:1;overflow-y:auto;padding-bottom:24px;width:100%}
  .pk-tabbar{display:none}
  /* Écart voulu vs parc : le sheet porte TOUS les formulaires du portail → visible en paysage (dialogue centré).
     Ne pas re-copier la règle parc à ge-109. Fermé, la règle de base le pousse hors-écran par
     translateY(105%) (calibré pour un sheet ancré en bas, mobile) — recentré en top:50%/left:50%, ce calcul
     ne sort plus l'élément de l'écran : sans opacity/pointer-events, le sheet « fermé » restait visible et
     cliquable en plein milieu de l'écran (constaté au rendu, 2026-09-11). */
  .pk-sheet{left:50%;right:auto;bottom:auto;top:50%;transform:translate(-50%,-42%);width:min(560px,90vw);border-radius:18px;opacity:0;pointer-events:none;transition:transform .22s ease,opacity .22s ease;max-height:calc(100vh - 48px)}
  .pk-sheet.open{transform:translate(-50%,-50%);opacity:1;pointer-events:auto}
  #pk-map{height:calc(100vh - 120px)}
}

/* ══ Atelier — composants (préfixe at-) ══ */
/* Avant connexion l'app est cachée ; après, la règle desktop ci-dessus (#app.pk-on{display:flex}, ≥900px) doit gagner
   sur la base #app.pk-on{display:block} du shell → on ne redéfinit PAS #app.pk-on ici (même spécificité, l'ordre décide). */
#app:not(.pk-on){display:none}
.at-hidden{display:none!important}
.at-page-head{margin:2px 2px 14px}
.at-eyeb{font-size:.64rem;font-weight:700;letter-spacing:.13em;text-transform:uppercase;color:#2c53b0;margin:0 0 3px}
.at-h1{font-family:'DM Sans',sans-serif;font-weight:800;font-size:1.5rem;letter-spacing:-.02em;color:var(--text);margin:0;line-height:1.2}
.at-sub{font-size:.78rem;color:var(--text3);margin:3px 0 0}
.at-h2{font-family:'DM Sans',sans-serif;font-weight:700;font-size:1.02rem;letter-spacing:-.01em;margin:0}
.at-h3{font-family:'DM Sans',sans-serif;font-weight:700;font-size:.9rem;margin:0}
.at-hsec{display:flex;align-items:baseline;justify-content:space-between;margin:4px 2px 10px}
.at-lnk{font-size:.78rem;font-weight:600;color:#2c53b0;background:none;border:none;font-family:inherit;cursor:pointer;padding:6px 0;min-height:48px;display:inline-flex;align-items:center}
.at-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:48px;padding:0 20px;border-radius:12px;font-weight:700;font-size:.92rem;border:none;font-family:inherit;cursor:pointer;white-space:nowrap}
.at-btn.p{background:var(--eld-blue);color:#fff}.at-btn.p:active{background:var(--eld-blue2)}
.at-btn.s{background:#fff;color:var(--text2);border:1.5px solid var(--border)}
.at-btn.soft{background:var(--eld-blue-soft);color:var(--eld-blue)}
.at-btn.d{background:var(--red-soft);color:var(--red)}
.at-btn.sm{min-height:48px;padding:0 14px;font-size:.82rem;border-radius:10px}
.at-btn.full{width:100%}
.at-btn svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex:none}
.at-seg{display:flex;gap:8px}
.at-seg>button{flex:1;min-height:48px;display:flex;align-items:center;justify-content:center;gap:6px;background:#fff;color:var(--text2);border:1.5px solid var(--border);border-radius:10px;font-size:.84rem;font-weight:600;padding:0 10px;font-family:inherit;cursor:pointer;white-space:nowrap}
.at-seg>button.on{background:var(--eld-blue);color:#fff;border-color:var(--eld-blue)}
.at-seg.tight{flex-wrap:wrap}
.at-seg.tight>button{font-size:.78rem;padding:0 6px;flex:1 1 auto}
/* [ge-177] Champ de choix obligatoire non renseigne. Doublé du toast qui dit quoi faire :
   la bordure seule ne suffirait pas (couleur = unique porteur d'information). */
.at-seg.manque>button{border-color:var(--red)}

.at-seg.grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}
.at-field{display:flex;flex-direction:column;gap:5px;margin-bottom:14px}
.at-field label{font-size:.72rem;color:var(--text2);font-weight:700;text-transform:uppercase;letter-spacing:.3px}
.at-field input,.at-field textarea,.at-field select{min-height:48px;padding:10px 14px;border:1.5px solid var(--border);border-radius:10px;font-size:16px;background:#fff;font-family:inherit;color:var(--text);width:100%}
.at-field textarea{min-height:84px;resize:vertical}
.at-field input:focus,.at-field textarea:focus{border-color:var(--eld-blue);outline:none}
.at-row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.at-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow)}
.at-list{display:flex;flex-direction:column;gap:8px}
.at-plate{font-family:'DM Sans',sans-serif;font-variant-numeric:tabular-nums;font-size:.82rem;font-weight:500;background:var(--eld-blue);color:#fff;border-radius:6px;padding:2px 8px;flex:none;white-space:nowrap;line-height:1.5}
.at-plate.soft{background:var(--eld-blue-soft);color:var(--eld-blue)}
/* [ge-143] .66rem (~10,6 px) → .72rem (~11,5 px), décision de Valentin du 26/09 : le chip porte l'information principale, lue à bout de bras sur tablette. */
.at-chip{font-size:.72rem;font-weight:700;border-radius:99px;padding:3px 9px;white-space:nowrap;letter-spacing:.02em;line-height:1.4;display:inline-block}
.at-chip.cat{background:#fff;color:var(--text3);border:1px solid var(--border)}
.at-chip.info{background:var(--eld-blue-soft);color:#2c53b0}.at-chip.warn{background:var(--orange-soft);color:var(--orange)}
.at-chip.crit{background:var(--red-soft);color:var(--red)}.at-chip.ok{background:var(--green-soft);color:var(--green)}.at-chip.mute{background:#eef1f6;color:var(--text3)}
/* Statuts de réception : chips PLEINS. Les chips pâles conviennent à une nuance (« ≤ 7 jours »,
   « Planifié ») ; l'état d'un bon de commande est l'information qu'on cherche en parcourant la
   liste, il doit se lire d'un coup d'œil à bout de bras sur une tablette d'atelier. Le bleu pâle
   `info` se confondait avec le gris `mute` qu'il remplaçait (retour terrain 2026-09-14). */
.at-chip.f-blue{background:var(--eld-blue);color:#fff}
.at-chip.f-orange{background:var(--orange);color:#fff}
.at-chip.f-green{background:var(--green);color:#fff}
/* Quatrième chip plein (Suivi GPS, 2026-09-14) : un boîtier HS n'est pas un camion garé.
   Réutiliser .f-orange pour les deux rendrait indiscernables « à l'arrêt » (normal, on
   attend) et « sans position » (matériel en panne, donc du travail pour l'atelier).
   Contraste texte blanc sur --red : 5,56:1. */
.at-chip.f-red{background:var(--red);color:#fff}
/* Cartes de liste OR et véhicule : même géométrie de rangées (r1 plaque/titre/n°, r2 chips).
   Sélecteurs partagés plutôt que dupliqués — la correction paysage des titres vaut donc pour les deux. */
.at-or,.at-veh,.at-lrow{display:flex;flex-direction:column;gap:8px;padding:12px 14px;cursor:pointer;text-align:left;width:100%;font-family:inherit}
.at-lrow .at-sub{margin:0}  /* le gap du flex porte l'espacement : sans ça, 8px + 3px de marge */
/* Même géométrie que .at-lrow mais SANS cursor:pointer : pour les cartes d'information qui ne
   sont pas cliquables. Un curseur main sur un bloc inerte promet une action qui n'existe pas. */
.at-lbox{display:flex;flex-direction:column;gap:8px;padding:12px 14px;text-align:left}
.at-lbox .at-sub{margin:0}
/* Objet d'un bon de commande : c'est lui qu'on cherche en parcourant la liste, il doit primer sur
   le fournisseur et le décompte de lignes — sans aller jusqu'au poids du numéro de BC. */
.at-bc-objet{font-size:.92rem;font-weight:600;color:var(--text);line-height:1.3}
.at-or.on,.at-veh.on{background:var(--eld-blue-soft);border-color:#b9c6e2}
.at-or .r1,.at-veh .r1{display:flex;align-items:center;gap:9px}
.at-or .tv,.at-veh .tv{font-size:.9rem;font-weight:600;color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.at-or .r2,.at-veh .r2{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.at-or .age,.at-veh .age{font-size:.72rem;color:var(--text3);margin-left:auto;white-space:nowrap}
/* [ge-190] L'âge décide de l'ordre de traitement : orange à 8 jours, rouge à 30 — seuils choisis
   par Valentin le 2026-09-22. Avant, « il y a 19 h » et « il y a 55 j » portaient le même gris,
   et c'est pour ça qu'un OR de juillet ne se remarquait pas. Jamais appliqué à l'onglet
   « Terminés », où l'ancienneté n'est pas une alerte mais une archive. */
.at-or .age.warn{color:var(--orange);font-weight:700}
.at-or .age.alert{color:var(--red);font-weight:700}
/* Avancement et cause du blocage : la carte répond à « où en est-il ? » sans ouvrir la fiche. */
.at-or .avct{font-family:'DM Sans',sans-serif;font-variant-numeric:tabular-nums;font-size:.72rem;color:var(--text3);white-space:nowrap}
.at-or .avct.done{color:var(--green);font-weight:700}
/* [ge-195] « Sur parc » : le camion est au dépôt en ce moment, c'est le moment de le prendre.
   Vert, comme dans la SPA — même information, même couleur sur les deux écrans. Au second plan depuis le polish du 18/10 (Valentin) :
   graisse normale et texte court, comme la GMAO — en gras 800, elle prenait le pas sur la plaque. */
.at-or .parc{display:inline-flex;align-items:center;background:var(--green-soft);color:var(--green);border-radius:99px;padding:0 6px;font-size:.68rem;line-height:1.5;font-weight:500;white-space:nowrap}
.at-or .parc.vieux{background:var(--orange-soft);color:var(--orange)} /* [ge-145] sur parc, position > 60 min */
/* [ge-280] Véhicule externe (client à refacturer) : contour magenta #be185d, la teinte libre du module — même choix que la GMAO
   (index.html, .or-externe). Contour, pas aplat : une nature, pas une urgence. */
.at-chip.ext{background:#fff;color:#be185d;border:1.5px solid #be185d;padding:2px 8px}
/* [ge-280, 16/10] Case « Véhicule externe » de « Travail réalisé » : même magenta que l'étiquette, cible ≥ 48 px. */
.at-ext-case{display:flex;align-items:center;gap:12px;min-height:48px;padding:8px 14px;margin:0 0 14px;border:1.5px solid #be185d;border-radius:12px;background:#fff;cursor:pointer}
.at-ext-case input{flex:none;width:22px;height:22px;margin:0;accent-color:#be185d}
.at-ext-case span{font-weight:500;color:var(--text2);line-height:1.35}
.at-ext-case b{font-weight:700;color:#be185d}
.at-or .cli{font-size:.74rem;font-weight:600;color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:180px}
.at-or .blk{flex-basis:100%;font-size:.74rem;color:var(--text3);line-height:1.35}
.at-or .num,.at-veh .num{font-family:'DM Sans',sans-serif;font-variant-numeric:tabular-nums;font-size:.7rem;color:var(--text3)}
.at-chk{display:flex;align-items:center;gap:12px;min-height:48px;padding:4px 0;border-bottom:1px solid var(--border);font-size:.9rem;font-weight:500;width:100%;background:none;border-left:0;border-right:0;border-top:0;font-family:inherit;text-align:left;cursor:pointer;color:var(--text)}
.at-chk:last-child{border-bottom:none}
.at-chk.done{color:var(--text3);text-decoration:line-through}
.at-box{width:26px;height:26px;border-radius:8px;border:2px solid #c3cad8;flex:none;display:flex;align-items:center;justify-content:center;background:#fff}
.at-box.on{background:var(--green);border-color:var(--green);color:#fff}
.at-box svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round}
.at-photos{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.at-photos>*{aspect-ratio:1;border-radius:10px;overflow:hidden;background:#dfe4ee;position:relative;display:block;border:none;padding:0;cursor:pointer}
.at-photos img{width:100%;height:100%;object-fit:cover;display:block}
.at-photos .add{background:var(--eld-blue-soft);color:var(--eld-blue);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-size:.7rem;font-weight:700;border:1.5px dashed #b9c6e2;font-family:inherit}
.at-photos .add svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:1.8}
.at-pagebar{height:56px;flex:none;background:var(--surface);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:6px;padding:0 10px 0 6px;position:sticky;top:0;z-index:2}
/* Le chevron de retour a la MEME forme partout : ecrans de detail (.at-pagebar) comme
   ecrans de liste doues d'un retour vers le Menu (.at-page-head). Un seul selecteur pour
   les deux — le dupliquer les ferait diverger au premier ajustement. */
.at-pagebar .back,.at-page-head .back{width:44px;height:44px;display:flex;align-items:center;justify-content:center;color:var(--eld-blue);background:none;border:none;cursor:pointer}
.at-pagebar .back svg,.at-page-head .back svg{width:24px;height:24px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.at-pagebar .t{font-family:'DM Sans',sans-serif;font-weight:800;font-size:1.05rem;letter-spacing:-.01em;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.at-footbar{flex:none;background:var(--surface);border-top:1px solid var(--border);padding:12px 18px calc(12px + env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:8px;position:sticky;bottom:0}
.at-footbar .row{display:flex;gap:8px}.at-footbar .row>.at-btn{flex:1}
.at-empty{padding:40px 20px;text-align:center;color:var(--text3);font-size:.9rem}
.at-note{font-size:.76rem;color:var(--text2);line-height:1.45}
.at-kv{display:flex;flex-wrap:wrap;gap:8px 12px;font-size:.82rem;color:var(--text2);align-items:center}
.at-act .row{display:flex;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid var(--border);min-height:48px}
.at-act .row:last-child{border-bottom:none}
/* [ge-112] lignes cliquables non natives (role=button) : le focus clavier se voit, comme sur un vrai bouton */
.at-act .row[tabindex]:focus-visible{outline:2px solid var(--eld-blue);outline-offset:-2px;border-radius:8px}
.at-act .a{font-size:.8rem;font-weight:600;color:var(--text)}.at-act .b2{font-size:.7rem;color:var(--text3)}
.at-search{display:flex;align-items:center;gap:10px;min-height:48px;padding:0 14px;border:1.5px solid var(--border);border-radius:10px;background:#fff;margin-bottom:10px}
.at-search input{border:none;outline:none;flex:1;font-size:16px;font-family:inherit;min-height:42px;background:transparent}
.at-search svg{width:20px;height:20px;stroke:var(--text3);fill:none;stroke-width:1.8;flex:none}
.at-toasts{position:fixed;left:50%;bottom:calc(96px + env(safe-area-inset-bottom));transform:translateX(-50%);display:flex;flex-direction:column;gap:8px;z-index:500;pointer-events:none}
.at-toast{background:#1a1f2e;color:#fff;border-radius:12px;padding:12px 18px;font-size:.88rem;font-weight:600;box-shadow:0 10px 30px rgba(0,0,0,.25);max-width:min(90vw,520px)}
.at-toast.err{background:var(--red)}
.at-sheet-h{font-family:'DM Sans',sans-serif;font-weight:800;font-size:1.15rem;margin:0 0 4px}
.at-sheet-sub{font-size:.8rem;color:var(--text3);margin:0 0 16px}
.at-chips{display:flex;gap:8px;flex-wrap:wrap}
.at-chips>button{min-height:48px;padding:0 14px;border-radius:99px;border:1.5px solid var(--border);background:#fff;color:var(--text2);font-weight:600;font-size:.84rem;font-family:inherit;cursor:pointer}
.at-chips>button.on{background:var(--eld-blue-soft);color:var(--eld-blue);border-color:#b9c6e2}
.at-tabbar-badge{position:absolute;top:0;right:calc(50% - 22px);font-size:.6rem;font-weight:700;background:var(--orange);color:#fff;border-radius:99px;padding:0 5px;line-height:1.5}
/* Fiche OR : page plein écran en portrait, volet droit en paysage (spec §4.2) */
#or-fiche{display:none;flex-direction:column;background:var(--bg)}
#or-fiche.open{display:flex;position:fixed;inset:0;z-index:200;overflow:auto}
/* Réserve de bas de page = hauteur de .at-footbar collée (mesurée 185 px sur le gabarit le plus haut,
   statuts valide/planifie ; stable de 390 à 960 px de large) + l'encoche que la footbar ajoute à la
   sienne. La footbar `position:sticky;bottom:0` occupe en permanence les 185 derniers px du scrollport :
   sans cette réserve, la fin de .at-body ne peut jamais être remontée au-dessus d'elle (la carte Pièces
   et son bouton « Sortir une pièce » de la tâche 11 sont les derniers arrivés dans cette zone).
   Coût assumé : +161 px de défilement par fiche, y compris les OR terminés, qui n'ont pas de footbar. */
#or-fiche .at-body{flex:1;padding:14px 16px calc(185px + env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:12px}
#tri-fiche,#veh-fiche,#ctl-fiche{display:none;flex-direction:column;background:var(--bg)}
#tri-fiche.open,#veh-fiche.open,#ctl-fiche.open{display:flex;position:fixed;inset:0;z-index:200;overflow:auto}
#tri-fiche .at-body,#veh-fiche .at-body,#ctl-fiche .at-body{flex:1;padding:14px 16px 24px;display:flex;flex-direction:column;gap:12px}
/* La fiche BC n'est pas un volet en overlay : elle vit dans le flux de sa section, d'où le scope
   à part (pas de flex:1 à occuper, une réserve basse pour la barre d'action fixe). Même padding
   et même rythme vertical que les volets, pour qu'une fiche reste une fiche d'un écran à l'autre. */
#sec-receptions .at-body{padding:14px 16px calc(96px + env(safe-area-inset-bottom));display:flex;flex-direction:column;gap:12px}
/* ══ Atelier — lot 2 : inventaire, historique pneus. La base (.stock-grid/.stock-card, .pk-fchips/.pk-fchip,
   .pk-kpis/.pk-kpi, .pk-day, .pk-mv, .pk-plate) est commune aux deux portails → portal-shell.css ;
   #pk-map-chips (carte parc) n'a jamais été porté. Ici : les surcharges tactiles et le scanner. ══ */
/* Lot 2 — retouches tactiles et scanner (préfixe at-) */
.pk-fchip{min-height:48px;display:inline-flex;align-items:center}
/* .stock-card est un <button> (tâche 12) — parc.php l'appliquait à un <div> (l.3432), jamais
   besoin de neutraliser le biseau UA par défaut des boutons. border:0 corrige le contour
   noir 2px outset visible sur chaque carte (revue tâche 12, Important 1). */
.stock-card{min-height:48px;border:0}
.at-scan-wrap{background:#000;border-radius:12px;overflow:hidden;position:relative;aspect-ratio:4/3;transition:outline .2s}
.at-scan-wrap video{width:100%;height:100%;object-fit:cover;display:block}
.at-scan-res{margin-top:10px;min-height:20px;font-size:.84rem}
.at-scan-res.ok{padding:10px;background:var(--green-soft);border-radius:8px;text-align:center;font-weight:600;color:var(--green)}
.at-scan-res.ko{padding:12px;background:var(--red-soft);border-radius:8px;text-align:center;color:var(--red);font-weight:700}
.at-scan-res.warn{padding:12px;background:var(--orange-soft);border-radius:8px;color:var(--orange)}
.at-piece-qty{font-family:'DM Sans',sans-serif;font-weight:800;font-size:1.6rem;letter-spacing:-.02em}
.at-piece-qty.ok{color:var(--green)}.at-piece-qty.warn{color:var(--orange)}.at-piece-qty.crit{color:var(--red)}
.pk-side .at-side-scan{margin:10px 14px 0;width:calc(100% - 28px)}
.at-side-scan{display:none}
/* ── Lot 4 bloc 1 : réception BL ──
   Le portail n'avait pas de stepper : .at-piece-qty n'est qu'une typo de quantité. Celui-ci est
   taillé pour des doigts gantés — cibles 48 px, comme .at-btn (spec lot 4 §2.7). Inspiré de
   .rcv-step de tools/parc.php, aux couleurs et aux variables du portail. */
.at-step{display:inline-flex;align-items:center;gap:4px}
.at-step button{width:48px;height:48px;border-radius:12px;border:1.5px solid var(--border);background:var(--surface);
  font-size:1.35rem;font-weight:700;color:var(--text);line-height:1;display:flex;align-items:center;justify-content:center;cursor:pointer}
.at-step button:disabled{opacity:.4;cursor:default}
.at-step b{min-width:46px;text-align:center;font-size:1.2rem;font-weight:800;font-variant-numeric:tabular-nums}
/* Ligne de proposition de réception ; `.rel` = reliquat, volontairement en retrait visuel. */
.at-rcv{display:flex;flex-direction:column;gap:8px}
.at-rcv.rel{opacity:.72}

/* Barre d'action des sections SANS volet (#sec-receptions, #sec-devis) — portrait.
   Le `position:sticky;bottom:0` de .at-footbar (règle de base plus haut) N'AGIT PAS ici : le conteneur de
   défilement est `body`, dont la hauteur vaut celle du CONTENU — `bottom` se mesure donc par
   rapport au bas du document, jamais atteint. La footbar se comporte en statique et se retrouve
   sous la tab bar FIXE (61 px + encoche, .pk-tabbar dans portal-shell.css) tant qu'on n'a pas fait défiler.
   Mesuré le 2026-09-13 sur la fiche Devis : bouton à 872-920, tab bar à partir de 875 — il
   PARAÎT cliquable, et un appui en son centre atteignait le FAB Scanner.
   Un `padding-bottom` sur la section ne corrige rien : il pousse le bas de la section, pas son
   dernier enfant. D'où `fixed`, au-dessus de la tab bar, avec la réserve correspondante.
   Les fiches en volet (#or-fiche, #tri-fiche…) ne sont pas concernées : leur volet est un vrai
   scrollport et leur réserve est déjà posée (#or-fiche .at-body plus haut). D'où le scope par identifiant. */
#sec-receptions .at-footbar,
#sec-devis .at-footbar{position:fixed;left:0;right:0;bottom:calc(61px + env(safe-area-inset-bottom));
  z-index:140;max-width:1400px;margin:0 auto}
/* Réserve = hauteur de la footbar (73 px mesurés : 48 de bouton + 12+12 de padding + bordure)
   + la tab bar. Sans elle, la fin du contenu passe sous la barre fixe. */
#sec-receptions,
#sec-devis{padding-bottom:calc(134px + env(safe-area-inset-bottom))}

/* ── Responsive : TOUJOURS après les règles de base (lessons.md 2026-09-04/05) ── */
@media (min-width:900px){
  /* Tab bar masquée et .pk-content .main devenu le scrollport : la footbar collante se cale
     correctement toute seule, le décalage portrait (bloc ci-dessus) n'a plus lieu d'être.
     Déclaré ICI et pas dans le bloc min-width:900px du haut de fichier : à specificité égale
     c'est le DERNIER qui gagne, et la règle de base est plus bas que lui
     (lessons.md 2026-09-04/05, « le responsive se déclare APRÈS la règle de base »). */
  /* Tab bar masquée et .pk-content .main devenu un vrai scrollport : .at-footbar retrouve son
     comportement collant normal. On annule TOUT le traitement portrait — position comprise.
     Déclaré ici et pas dans le bloc min-width:900px du haut de fichier : à spécificité égale
     c'est le dernier qui gagne, et la règle de base est plus bas que lui
     (lessons.md 2026-09-04/05, « le responsive se déclare APRÈS la règle de base »). */
  #sec-receptions,
  #sec-devis{padding-bottom:0}
  #sec-receptions .at-footbar,
  #sec-devis .at-footbar{position:sticky;bottom:0;left:auto;right:auto;z-index:auto;max-width:none}
  .at-split{display:grid;grid-template-columns:320px minmax(0,1fr);gap:14px;align-items:start}
  #or-fiche,#tri-fiche,#veh-fiche,#ctl-fiche{display:flex;position:sticky;top:0;max-height:calc(100vh - 60px);overflow:auto;border:1px solid var(--border);border-radius:16px;background:var(--surface)}
  /* La liste défile POUR ELLE-MÊME, comme la fiche. Sans ça, c'est la page entière qui scrolle :
     la grille prend la hauteur de la plus grande colonne, la fiche reste collée en haut, et il
     reste un vide sous elle dans la colonne droite — mesuré avec 28 véhicules : grille 3906 px,
     fiche 516, soit 3390 px de blanc (retour terrain 2026-09-12 : « un trou à droite, pas beau »).
     En prod la liste fait 227 véhicules : le trou est la règle, pas l'exception.
     `overscroll-behavior:contain` évite que le geste de fin de liste entraîne la page. */
  #or-list-pane,#tri-list-pane,#veh-list-pane,#ctl-list-pane{max-height:calc(100vh - 60px);overflow-y:auto;overscroll-behavior:contain}
  #or-fiche.open,#tri-fiche.open,#veh-fiche.open,#ctl-fiche.open{position:sticky;inset:auto;z-index:auto}
  #or-fiche .at-pagebar .back,#tri-fiche .at-pagebar .back,#veh-fiche .at-pagebar .back,#ctl-fiche .at-pagebar .back{display:none}
  .at-tiles-4{grid-template-columns:repeat(4,1fr)}
  .pk-side .at-side-scan{display:inline-flex}
  /* Tablette en paysage (960×576 mesuré, ge-120 ; ce bloc a été écrit en supposant 960×600 —
     l'écart de 24 px aggrave le symptôme, il ne l'invalide pas) : le viseur 4/3 pleine largeur
     du sheet fait 393 px et repoussait
     la saisie manuelle et « Fermer » sous le pli interne du dialogue. On borne le VISEUR, pas le
     dialogue — « saisie manuelle toujours possible » (spec §4.5) doit rester vrai sans scroller.
     aspect-ratio:auto + height : un simple max-height sur un bloc en aspect-ratio 4/3 fait suivre la
     LARGEUR (mesuré : 304 px au lieu de 524, viseur désaxé et moins de pixels horizontaux pour ZXing).
     Ici le viseur garde toute la largeur du sheet et object-fit:cover rogne la hauteur — format large,
     le bon pour un code-barres 1D. */
  .at-scan-wrap{aspect-ratio:auto;height:38vh}
}

/* ── Tablette en paysage : large (960) mais COURT (576, et 258 clavier ouvert). Le bloc
   min-width:900px ci-dessus applique une coquille de bureau qui suppose aussi de la hauteur ;
   ces surcharges ne visent que ce cas. Rien n'est édité, tout est surchargé : desktop et
   portrait restent le code d'avant. Spec : docs/superpowers/specs/2026-09-11-portail-atelier-paysage-design.md ── */
@media (min-width:900px) and (max-height:700px){
  /* Rail : la navigation se paie en largeur, jamais en hauteur (0 px pris sur les 576) */
  .pk-side{width:72px;padding:14px 6px 12px;align-items:center}
  .pk-nav-l{display:none}
  .pk-nav-i{justify-content:center;width:52px;padding:10px 0;gap:0;position:relative}
  /* Badge du rail : un COMPTEUR D'ANGLE, pas la pilule d'une ligne à libellé. Mesuré : le bouton
     fait 52×39 et l'icône occupe 17..36 × 10..29 ; la pilule de 25×19 posée à right:4px retombait
     donc en plein milieu de l'icône (retour terrain 2026-09-12 : « les nombres sont décalés »).
     Ancré au coin haut-droit, dimensions fixes, chiffres centrés. Fond OPAQUE : le
     rgba(255,255,255,.16) hérité de la sidebar ne se lit pas sur le dégradé du rail. */
  .pk-nav-i .pk-ct{position:absolute;top:0;right:0;margin-left:0;min-width:17px;height:17px;padding:0 4px;
                   display:flex;align-items:center;justify-content:center;font-size:.6rem;line-height:1;
                   border-radius:9px;background:#fff;color:var(--eld-blue)}
  /* Les variantes portent leur fond depuis le bloc sidebar ; on redonne le texte, sinon elles
     héritent du `color` ci-dessus (même spécificité, déclaré plus tard) et virent illisibles. */
  .pk-nav-i .pk-ct.warn,.pk-nav-i .pk-ct.crit{color:#fff}
  .pk-slogo{width:52px;margin-bottom:0}
  .pk-tagline{display:none}
  .pk-me{justify-content:center;gap:0}
  .pk-me .a,.pk-me .b{display:none}
  .pk-side .at-side-scan{width:52px;margin:10px 0 0;padding:12px 0;font-size:.7rem}
  /* Colonnes : liste inchangée, la fiche récupère les 150 px rendus par le rail (346 -> 496) */
  .at-split{grid-template-columns:320px minmax(0,1fr)}
  /* Titres : le plan prescrivait `white-space:normal;overflow:visible;text-overflow:clip`.
     MESURÉ : ça ne tient pas. La cause n'est pas l'enroulement, c'est la LARGEUR — dans une
     liste de 320, la rangée `.r1` répartit plaque 87 + gouttières 18 + numéro 101, et il reste
     84 px au titre. Enroulé sur 84 px, « Remplacement » (105 px, insécable) débordait de 21 px
     et, `overflow:visible` aidant, RECOUVRAIT le numéro d'OR sur 12 px.
     Correctif : le titre prend sa propre ligne sous plaque+numéro (290 px, une seule ligne).
     On ne surcharge plus overflow/text-overflow : la base `overflow:hidden` reste, donc rien ne
     peut déborder ; `overflow-wrap:anywhere` casse le mot pathologique plutôt que de le rogner
     (vérifié sur un mot de 56 caractères : débord 0, aucun chevauchement). */
  /* La hauteur est la ressource rare en paysage (576 px) : une carte de 260 px prendrait la
     moitié d'une fiche de 516. On la ramène à 160 — elle situe, elle ne détaille pas. */
  #pk-minimap{height:160px}
  .at-or .r1,.at-veh .r1{flex-wrap:wrap}
  .at-or .tv,.at-veh .tv{flex-basis:100%;order:3;white-space:normal;overflow-wrap:anywhere}
  /* Boutons d'action des FICHES empilés : le débordement devient structurellement impossible,
     au lieu d'être évité de 20 px. SCOPÉ aux fiches : .at-footbar .row sert aussi aux boutons
     Annuler/Valider des feuilles de saisie, qui doivent rester côte à côte. */
  #or-fiche .at-footbar .row,#tri-fiche .at-footbar .row,#veh-fiche .at-footbar .row,#ctl-fiche .at-footbar .row{flex-direction:column}
  /* Feuilles ancrées en bas, pleine largeur : clavier ouvert il ne reste que 258 px visibles,
     et cette fenêtre se déplace pour suivre le champ touché. Ancrée en bas, la feuille place
     son bouton à ~24 px du bord bas — donc dans la fenêtre. Centrée, il en sort.
     opacity/pointer-events se restaurent AVEC l'ancrage : le bloc min-width:900px les avait
     neutralisés parce qu'une feuille centrée n'est plus masquée par translateY(105%). */
  .pk-sheet{left:0;right:0;top:auto;bottom:0;width:auto;border-radius:22px 22px 0 0;
            max-height:calc(100vh - 24px);transform:translateY(105%);opacity:1;pointer-events:auto}
  .pk-sheet.open{transform:none}
  /* Champs sur une grille auto-fit : 924 px utiles -> 4 colonnes de ~231 px, donc une seule
     rangée pour un formulaire de 4 champs. select/textarea/footbar gardent la pleine largeur :
     un select d'OR dans 231 px est inutilisable.
     :has() sans réserve : Chrome >= 105, la sonde a relevé Chrome 152 sur la tablette. */
  #at-sheet-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:0 12px}
  #at-sheet-form .at-field:has(select),#at-sheet-form .at-field:has(textarea),
  #at-sheet-form .at-footbar{grid-column:1/-1}
}

/* ── Suivi GPS (2026-09-14) ──────────────────────────────────────────────────────────────
   Le liseré de 4 px donne l'état au balayage vertical, avant même la lecture du chip :
   c'est l'usage réel de cet écran (« qui est sur site ? » en trois secondes, à bout de bras,
   sur 224 lignes). Il encode une information, il ne décore pas.
   Sa couleur est celle du chip de la même ligne — une seule source de vérité par état
   (gpsEtat() dans gps.js), sans quoi deux verts légèrement différents se liraient comme
   deux états distincts. */
.gps-row{border-left:4px solid transparent}
.gps-site{border-left-color:var(--green)}
.gps-route{border-left-color:var(--eld-blue)}
.gps-arret{border-left-color:var(--orange)}
.gps-hs{border-left-color:var(--red)}
.gps-l1{display:flex;align-items:center;gap:8px;justify-content:space-between}
.gps-l2{font-size:.82rem;color:var(--text2)}
.gps-l3{display:flex;justify-content:space-between;gap:8px;font-size:.78rem;color:var(--text3)}
/* Chasse fixe sur la colonne de droite : elle se rafraîchit toutes les 60 s, et en chasse
   proportionnelle les chiffres dansent d'un demi-caractère à chaque cycle. */
.gps-num{font-family:'DM Sans',sans-serif;font-variant-numeric:tabular-nums;font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right}
/* Carte plein cadre de la section Suivi GPS. La hauteur est la ressource rare en paysage
   (576 px, 258 clavier ouvert) : on prend la place restante plutôt qu'une hauteur fixe, qui
   déborderait dans une orientation ou gaspillerait l'écran dans l'autre.
   #gps-map et non #pk-minimap : ce dernier est un id UNIQUE, déjà pris par la fiche véhicule —
   deux cartes portant le même id se voleraient leur conteneur. */
#gps-map{height:calc(100vh - 250px);min-height:240px;border-radius:10px;overflow:hidden;background:#e4e9ec;isolation:isolate}
@media (min-width:900px){#gps-map{height:calc(100vh - 210px)}}
/* Contrôles de zoom de la carte GPS. Leaflet les dessine à 30 px : bien en dessous du
   plancher de 48 px que ce portail impose parce qu'on l'utilise avec des gants (app.css:15-16).
   Scopé à #gps-map — la mini-carte de la fiche véhicule est volontairement non interactive et
   n'a aucun contrôle à agrandir. */
#gps-map .leaflet-control-zoom a{width:48px;height:48px;line-height:48px;font-size:1.3rem}
/* Dans un en-tete de LISTE le chevron n'a pas de barre pour l'aligner : on le sort du
   padding de la carte et on lui rend sa marge basse, sinon il colle au titre. */
.at-page-head .back{margin:0 0 2px -10px}
