/* KL Debugger Hub — thème sombre premium (aligné sur le HUD KL Debugger Pro), mobile-first */

:root {
  /* Fond : trois profondeurs, du plus sombre (hors panneau) au plus clair (creux). */
  --bg: #080b13;
  --bg-2: #121a2b;
  --card: #0e1420;
  --card-2: #131b2b;
  --border: #1f2a3f;
  --border-lit: #2c3d5c;

  --fg: #e9eef8;
  --muted: #7f8ba6;
  --dim: #566179;

  /* Cyan = instrument, violet = commande. Le dégradé des deux signe l'interface. */
  --accent: #35d0f2;
  --accent-2: #7c6cf6;
  --accent-soft: rgba(53, 208, 242, .13);

  --success: #34d399;
  --danger: #fb5f7a;
  --warn: #fbbf24;
  --orange: #fb923c;

  --radius: 12px;
  --radius-sm: 8px;

  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-brand: 'Space Grotesk', var(--font);
  /* Les chiffres passent en chasse fixe : c'est ce qui fait lire « instrument ». */
  --font-num: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --glow: 0 0 0 1px rgba(53, 208, 242, .25), 0 0 18px rgba(53, 208, 242, .18);
  --lift: 0 10px 30px rgba(0, 0, 0, .45);
}

/* Le mouvement est un confort, jamais une information. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  /* Quatre couches : deux halos, une grille d'instrument très discrète, le fond.
     La grille donne l'échelle et la sensation de tableau de bord sans bruit. */
  background-image:
    radial-gradient(1100px 520px at 82% -12%, rgba(53, 208, 242, .10), transparent 70%),
    radial-gradient(900px 460px at 8% 108%, rgba(124, 108, 246, .09), transparent 70%),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, .016) 0 1px, transparent 1px 44px),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, .016) 0 1px, transparent 1px 44px);
  background-attachment: fixed;
  color: var(--fg);
  font-family: var(--font);
  font-size: 14.5px;
  line-height: 1.5;
  min-height: 100vh;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { font-family: var(--font-brand); font-weight: 600; letter-spacing: .01em; }
h1 { font-size: 23px; margin: 0 0 4px; }
/* Un titre de panneau s'annonce par un trait lumineux, comme une étiquette gravée. */
h2 { font-size: 15px; margin: 0 0 14px; text-transform: uppercase; letter-spacing: .13em; color: var(--fg); display: flex; align-items: center; gap: 10px; }
h2::before { content: ''; width: 3px; height: 13px; border-radius: 2px; background: linear-gradient(180deg, var(--accent), var(--accent-2)); box-shadow: 0 0 10px rgba(53, 208, 242, .55); flex: none; }
/* h3 = étiquette de section. Un TITRE DE CONTENU garde sa casse et sa couleur :
   passer un intitulé de ticket en capitales grisées le rend illisible. */
h3 { font-size: 13px; margin: 0 0 8px; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
h3.tk-title, .tk-title { text-transform: none; letter-spacing: 0; color: var(--fg); font-family: var(--font); font-size: 13.5px; }
.page-sub { color: var(--muted); margin: 0 0 18px; font-size: 13px; }

/* Nom propre du parc : une boutique ou un client s'affiche TOUJOURS en capitales.
   C'est un effet d'affichage et rien d'autre — le texte réel garde sa casse, donc
   le copier-coller, la recherche dans la page et les lecteurs d'écran restent
   justes. Jamais de strtoupper en base ni dans une entité.
   Un nom déjà mis en capitales par un style hérité (h2, h3, .badge, .msg-ou,
   .ml-site, .tk-site, en-têtes de tableau) ne porte PAS cette classe : elle n'y
   ajouterait rien. Les attributs `title=` restent en casse d'origine : le CSS ne
   les atteint pas, et le nom y est le plus souvent pris dans une phrase. */
.nom-propre { text-transform: uppercase; }

.container { max-width: 1180px; margin: 0 auto; padding: 20px 16px 60px; }

/* ---------- Cartes & grilles ---------- */
.card {
  position: relative;
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 16px;
  box-shadow: var(--lift);
}
/* Arête haute éclairée : donne l'épaisseur d'un panneau, pas d'une boîte plate. */
.card::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 1px;
  border-radius: var(--radius) var(--radius) 0 0;
  background: linear-gradient(90deg, transparent, rgba(53, 208, 242, .45), rgba(124, 108, 246, .35), transparent);
  pointer-events: none;
}
.card.pad-sm { padding: 12px 14px; }
.grid { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .grid-2 { grid-template-columns: 1fr 1fr; } .grid-3 { grid-template-columns: repeat(2, 1fr); } .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .grid-3 { grid-template-columns: repeat(3, 1fr); } .grid-4 { grid-template-columns: repeat(4, 1fr); } }

/* Cadran : l'étiquette au-dessus, la valeur en chasse fixe, un liseré d'état. */
.kpi { text-align: left; overflow: hidden; }
.kpi::after {
  content: ''; position: absolute; left: 0; top: 12px; bottom: 12px; width: 2px;
  border-radius: 0 2px 2px 0; background: var(--accent); box-shadow: 0 0 12px var(--accent);
}
.kpi .kpi-label { color: var(--muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: .14em; font-weight: 600; }
.kpi .kpi-value { font-family: var(--font-num); font-size: 30px; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; margin-top: 4px; line-height: 1.1; }
.kpi .kpi-value small { font-family: var(--font); font-size: 13px; color: var(--muted); font-weight: 500; letter-spacing: 0; }
.kpi.good .kpi-value { color: var(--success); }
.kpi.good::after { background: var(--success); box-shadow: 0 0 12px var(--success); }
.kpi.warn .kpi-value { color: var(--warn); }
.kpi.warn::after { background: var(--warn); box-shadow: 0 0 12px var(--warn); }
.kpi.bad .kpi-value { color: var(--danger); }
.kpi.bad::after { background: var(--danger); box-shadow: 0 0 12px var(--danger); }

/* ---------- Boutons ---------- */
/* Touche de console : coin biseauté, arête lumineuse, enfoncement au clic. */
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 10px 18px; border: 0; cursor: pointer;
  border-radius: var(--radius-sm);
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
  font: 600 12.5px var(--font); letter-spacing: .06em; text-transform: uppercase; color: #06121a;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow: 0 4px 18px rgba(53, 208, 242, .28);
  text-decoration: none;
  transition: transform .1s ease, box-shadow .15s ease, filter .15s ease;
}
.btn:hover { filter: brightness(1.1); box-shadow: 0 6px 26px rgba(53, 208, 242, .42); text-decoration: none; }
.btn:active { transform: translateY(1px); box-shadow: 0 2px 10px rgba(53, 208, 242, .3); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn.btn-ghost {
  background: linear-gradient(180deg, rgba(255, 255, 255, .04), transparent), var(--bg-2);
  border: 1px solid var(--border-lit); color: var(--fg); box-shadow: none;
}
.btn.btn-ghost:hover { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 16px rgba(53, 208, 242, .18); }
.btn.btn-danger { background: linear-gradient(135deg, #fb5f7a, #ff9060); color: #1a0a10; box-shadow: 0 4px 18px rgba(251, 95, 122, .3); }
.btn.btn-danger:hover { box-shadow: 0 6px 26px rgba(251, 95, 122, .45); }
.btn.btn-sm { padding: 7px 12px; font-size: 11px; clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px); }
.btn[disabled] { opacity: .4; cursor: not-allowed; filter: grayscale(.5); box-shadow: none; }

/* ---------- Formulaires ---------- */
label { display: block; font-size: 12.5px; color: var(--muted); margin: 0 0 5px; font-weight: 500; }
input[type=text], input[type=email], input[type=password], input[type=url], input[type=number],
input[type=date], input[type=search], select, textarea {
  width: 100%; background: var(--bg-2); color: var(--fg);
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 13px; font: 400 13.5px var(--font); line-height: 1.45;
  outline: none;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: var(--glow); }
input::placeholder, textarea::placeholder { color: var(--dim); }
label { text-transform: uppercase; letter-spacing: .09em; font-size: 10.5px; font-weight: 600; }
textarea { min-height: 90px; resize: vertical; }
select { appearance: none; -webkit-appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%); background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%; background-size: 5px 5px; background-repeat: no-repeat; padding-right: 34px; }
input[type=checkbox] { accent-color: var(--accent); width: 16px; height: 16px; }
.field { margin-bottom: 14px; }
.form-row { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .form-row-2 { grid-template-columns: 1fr 1fr; } .form-row-3 { grid-template-columns: repeat(3, 1fr); } }
.help { font-size: 12px; color: var(--muted); margin-top: 4px; }

/* ---------- Tableaux ---------- */
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); }
table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
th { text-align: left; padding: 10px 14px; color: var(--muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: .12em; font-weight: 700; background: rgba(18, 26, 43, .8); white-space: nowrap; border-bottom: 1px solid var(--border-lit); }
td { padding: 11px 14px; border-top: 1px solid var(--border); vertical-align: middle; }
tr.rowlink { cursor: pointer; transition: background .12s ease; }
/* Une ligne survolée s'allume par la gauche : on sait où l'on pointe. */
tr.rowlink:hover td { background: rgba(53, 208, 242, .07); }
tr.rowlink:hover td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
td.num, th.num { text-align: right; font-family: var(--font-num); font-variant-numeric: tabular-nums; }

/* ---------- Badges / pastilles ---------- */
.badge { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 4px; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .09em; background: var(--bg-2); border: 1px solid var(--border-lit); color: var(--muted); white-space: nowrap; }
.badge.ok { color: var(--success); border-color: rgba(52, 211, 153, .45); background: rgba(52, 211, 153, .1); box-shadow: inset 0 0 12px rgba(52, 211, 153, .12); }
.badge.warn { color: var(--warn); border-color: rgba(251, 191, 36, .45); background: rgba(251, 191, 36, .1); box-shadow: inset 0 0 12px rgba(251, 191, 36, .12); }
.badge.bad { color: var(--danger); border-color: rgba(251, 95, 122, .45); background: rgba(251, 95, 122, .1); box-shadow: inset 0 0 12px rgba(251, 95, 122, .12); }
.badge.accent { color: var(--accent); border-color: rgba(53, 208, 242, .45); background: var(--accent-soft); box-shadow: inset 0 0 12px rgba(53, 208, 242, .12); }

/* Diodes d'état : une vraie lampe, avec halo ; celle qui signale une panne clignote. */
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.dot.p3 { background: var(--danger); box-shadow: 0 0 9px var(--danger); }
.dot.p2 { background: var(--warn); box-shadow: 0 0 9px var(--warn); }
.dot.p1 { background: var(--accent); box-shadow: 0 0 9px var(--accent); }
.dot.up { background: var(--success); box-shadow: 0 0 10px rgba(52, 211, 153, .85); }
.dot.down { background: var(--danger); box-shadow: 0 0 10px rgba(251, 95, 122, .9); animation: pouls 1.4s ease-in-out infinite; }
.dot.unknown { background: var(--dim); }

/* ---------- Jauge santé (anneau conic) ---------- */
.health-ring { --p: 0; --ring: var(--success); width: 64px; height: 64px; border-radius: 50%; flex-shrink: 0;
  background: conic-gradient(var(--ring) calc(var(--p) * 1%), rgba(255, 255, 255, .05) 0);
  display: flex; align-items: center; justify-content: center; position: relative;
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--ring) 45%, transparent)); }
/* Creux central + graduations fines : un cadran, pas un camembert. */
.health-ring::before { content: ''; position: absolute; inset: 6px; border-radius: 50%;
  background: radial-gradient(circle at 50% 35%, var(--card-2), var(--card));
  border: 1px solid var(--border); }
.health-ring::after { content: ''; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  background: repeating-conic-gradient(rgba(0, 0, 0, .55) 0deg 1.6deg, transparent 1.6deg 15deg);
  -webkit-mask: radial-gradient(circle, transparent 0 68%, #000 68%);
  mask: radial-gradient(circle, transparent 0 68%, #000 68%); }
.health-ring span { position: relative; font-family: var(--font-num); font-weight: 700; font-size: 16px; font-variant-numeric: tabular-nums; }
.health-ring.warn { --ring: var(--warn); } .health-ring.bad { --ring: var(--danger); }
.health-ring.lg { width: 96px; height: 96px; } .health-ring.lg span { font-size: 24px; }
.health-ring.na { --ring: rgba(255, 255, 255, .07); filter: none; } .health-ring.na span { color: var(--muted); font-size: 13px; }

/* ---------- Cartes site (dashboard) ---------- */
.site-card { display: block; color: inherit; }
.site-card { transition: transform .14s ease, border-color .14s ease, box-shadow .14s ease; }
.site-card:hover { text-decoration: none; border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 10px 34px rgba(53, 208, 242, .16), var(--lift); }
.site-card .site-head { display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }
.site-card .site-title { min-width: 0; }
.site-card .site-title strong { display: block; font-size: 15.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.site-card .site-title small { color: var(--muted); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
.site-metrics { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.site-metrics .m { background: linear-gradient(180deg, rgba(255, 255, 255, .03), transparent), var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 10px; }
.site-metrics .m b { display: block; font-family: var(--font-num); font-size: 16px; font-variant-numeric: tabular-nums; }
.site-metrics .m span { font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; font-weight: 600; }
.site-metrics .m.bad b { color: var(--danger); }
.site-metrics .m.warn b { color: var(--warn); }

/* ---------- Tickets ---------- */
.ticket-row { border-left: 3px solid var(--accent); }
.ticket-row.p3 { border-left-color: var(--danger); }
.ticket-row.p2 { border-left-color: var(--warn); }
.ticket-row.validate { border-left-color: var(--success); }
.section-pill { font-size: 11.5px; padding: 2px 9px; border-radius: 999px; background: var(--bg-2); border: 1px solid var(--border); color: var(--muted); }
.section-pill.todo { color: var(--accent); border-color: rgba(109, 139, 255, .4); }
.section-pill.validate { color: var(--success); border-color: rgba(55, 211, 153, .4); }
.comment { background: var(--bg-2); border: 1px solid var(--border); border-radius: 12px; padding: 10px 14px; margin-bottom: 10px; }
.comment.hub { border-color: rgba(109, 139, 255, .45); background: rgba(109, 139, 255, .07); }
.comment .meta { font-size: 11.5px; color: var(--muted); margin-bottom: 4px; }
.ticket-desc { background: var(--bg-2); border-radius: 12px; padding: 12px 16px; overflow-x: auto; }
.ticket-desc img { max-width: 100%; }

/* ---------- Flash ---------- */
/* Message système : barre d'état à liseré gauche, comme une alarme de tableau de bord. */
.flash { position: relative; padding: 12px 16px 12px 18px; border-radius: var(--radius-sm); margin-bottom: 14px; font-size: 13.5px; border: 1px solid; overflow: hidden; }
.flash::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: currentColor; box-shadow: 0 0 14px currentColor; }
.flash.success { background: rgba(52, 211, 153, .09); border-color: rgba(52, 211, 153, .4); color: var(--success); }
.flash.error { background: rgba(251, 95, 122, .09); border-color: rgba(251, 95, 122, .4); color: var(--danger); }
.flash.info { background: var(--accent-soft); border-color: rgba(53, 208, 242, .4); color: var(--accent); }
.flash.warn { background: rgba(251, 191, 36, .09); border-color: rgba(251, 191, 36, .4); color: var(--warn); }

/* ---------- Auth ---------- */
.auth-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
.auth-box { width: 100%; max-width: 420px; }
.auth-box .brand { display: block; text-align: center; font-size: 15px; margin-bottom: 6px; }
.auth-box .tagline { text-align: center; color: var(--muted); font-size: 12.5px; margin: 0 0 24px; }

/* ---------- Divers ---------- */
.toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 16px; }
.toolbar form { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.toolbar select, .toolbar input { width: auto; min-width: 130px; }
.spacer { flex: 1; }
.muted { color: var(--muted); }
.small { font-size: 12px; }
.mono { font-family: var(--font-num); font-size: 12.5px; }
.num { font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.nav-badge, .tk-age, .ml-age { font-family: var(--font-num); }
.token-box { background: var(--bg-2); border: 1px dashed var(--border); border-radius: 10px; padding: 10px 14px; font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; word-break: break-all; user-select: all; }
.empty { text-align: center; color: var(--muted); padding: 40px 16px; }
.empty .big { font-size: 34px; margin-bottom: 8px; }
.stale-banner { font-size: 12px; color: var(--warn); }
.nav-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--danger); color: #fff; font-size: 11px; font-weight: 700; vertical-align: middle; }
.pageactions { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 18px; }
.right { text-align: right; }
::-webkit-scrollbar { height: 8px; width: 8px; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

/* --- Tickets : grille de cartes (dense, lisible d'un coup d'œil) --- */
.tk-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
    gap: 12px;
    margin: 4px 0 14px;
}
.tk-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 11px 13px 10px 15px;
    background: var(--card, #151a26);
    border: 1px solid var(--border, #2a3142);
    border-radius: 12px;
    cursor: pointer;
    transition: transform .12s ease, border-color .12s ease, box-shadow .12s ease;
}
/* La priorité se lit à la couleur du liseré, sans occuper de place. */
.tk-card::before {
    content: '';
    position: absolute;
    left: 0; top: 10px; bottom: 10px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--accent);
}
.tk-card.p3::before { background: var(--danger); }
.tk-card.p2::before { background: var(--warn); }
.tk-card:hover {
    transform: translateY(-2px);
    border-color: var(--accent);
    box-shadow: 0 8px 22px rgba(0, 0, 0, .35);
}
.tk-head { display: flex; align-items: center; gap: 8px; font-size: 11.5px; }
.tk-site {
    font-weight: 600;
    color: var(--fg, #e8ecf5);
    opacity: .85;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tk-age { margin-left: auto; color: var(--muted, #8a8f98); white-space: nowrap; }
.tk-age.warn { color: var(--warn); }
.tk-card.stale { border-style: dashed; }
.tk-title {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.35;
    /* Deux lignes maximum : les cartes gardent la même hauteur, la grille reste calme. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.tk-foot { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11px; }
.tk-meta { color: var(--muted, #8a8f98); }
/* Lien étiré : le titre est un vrai lien, mais sa zone de clic couvre la carte
   entière. Un seul arrêt de tabulation par ticket, et les boutons d'action
   restent cliquables grâce au z-index supérieur ci-dessous. */
.tk-lien { color: inherit; }
.tk-lien:hover { text-decoration: none; color: var(--accent); }
.tk-lien::after { content: ''; position: absolute; inset: 0; z-index: 1; border-radius: 12px; }
/* L'anneau de focus doit cerner la CARTE, pas les trois mots du titre. */
.tk-lien:focus-visible { outline: none; }
.tk-lien:focus-visible::after { outline: 2px solid var(--accent); outline-offset: 2px; }

.tk-actions { display: flex; gap: 4px; opacity: 0; transition: opacity .12s ease; position: relative; z-index: 2; }
.tk-card:hover .tk-actions, .tk-actions:focus-within { opacity: 1; }
.tk-act {
    background: transparent;
    border: 1px solid var(--border, #2a3142);
    color: var(--fg, #e8ecf5);
    border-radius: 6px;
    padding: 1px 7px;
    font-size: 12px;
    cursor: pointer;
    line-height: 1.5;
}
.tk-act:hover { border-color: var(--accent); color: var(--accent); }
/* Sur écran tactile, les actions restent visibles : il n'y a pas de survol. */
@media (hover: none) {
    .tk-actions { opacity: 1; }
}

/* ============ Dashboard : grille des encarts ============ */
/* Depuis le 24/09/2026, chaque utilisateur choisit l'ORDRE et la LARGEUR des encarts
   (mode « Disposer », App\Service\DashboardLayout).
   ORDRE = ordre du DOM, rendu par le serveur : ni `order` ni `dense`, l'ordre de lecture
   et de tabulation reste l'ordre visuel.
   LARGEUR = propriété --dw-<id> posée sur .dash (jamais sur la section : les encarts
   tickets/temps se remplacent en entier quand ils se rechargent). Absente = « Auto » =
   la place d'origine, qui dépend de l'écran (replis des var() ci-dessous).
   Mobile-first : une colonne empilée sous 900 px, où aucune largeur ne s'applique. */
.dash { display: grid; gap: 14px; grid-template-columns: minmax(0, 1fr); }
.dash > .card { margin-bottom: 0; }
/* Un item de grille vaut min-content par défaut : tableau, piste météo, hash élargiraient
   la piste et TOUTE la page partirait en travers (mesuré : 1052 px sur 375, voir Météo). */
.dash > .card { min-width: 0; }
@media (min-width: 900px) {
    .dash { grid-template-columns: repeat(24, minmax(0, 1fr)); align-items: start; }
    .dash > .card               { grid-column: span 24; }   /* filet : encart inconnu = toute la ligne */
    .dash > .dash-parc          { grid-column: span var(--dw-parc, 24); }
    .dash > .dash-tickets       { grid-column: span var(--dw-tickets, 24); }
    .dash > .dash-temps         { grid-column: span var(--dw-temps, 24); }
    .dash > .dash-agenda        { grid-column: span var(--dw-agenda, 24); }
    .dash > .dash-alertes       { grid-column: span var(--dw-alertes, 24); }
    .dash > .dash-securite      { grid-column: span var(--dw-securite, 24); }
    .dash > .dash-trafic        { grid-column: span var(--dw-trafic, 24); }
    .dash > .dash-weather       { grid-column: span var(--dw-weather, 24); }
}
/* Encarts retirés, leurs règles avec eux (18/10/2026) : Note secrète et Hash PrestaShop
   (28/09/2026, palette ⌘K), Klorel, Argent et Activité (01/10/2026, modale Stats), Factures
   non payées et Raccourcis (18/10/2026). Au-delà de 1400 px, Alertes 1/3 + Dernières actions
   2/3 : public/css/dashboard.css, avec la place de « Dernières actions ». */
/* Mises en page internes réglées sur la largeur de L'ENCART, pas de l'écran : un encart
   réduit à 1/3 ne doit pas afficher 8 cartes de tickets écrasées. Seuils = anciens seuils
   d'écran moins marges et padding (−78 sous 900 px, −86 au-delà), pour qu'un encart en
   pleine largeur garde exactement son rendu d'avant. */
.tk-encart, .tp-encart, .dash-agenda, .dash-pshash { container: encart / inline-size; }
.wk { font-size: 11px; color: var(--muted); font-weight: 500; letter-spacing: .06em; }

/* --- Semaine --- */
.wk-grid { display: grid; gap: 8px; grid-template-columns: 1fr; }
/* 7 colonnes seulement quand « mercredi 24 » tient dans sa case (≈ 84 px + padding) ; entre
   les deux, 4 colonnes plutôt que des en-têtes qui débordent (mesuré : agenda à 1/3). */
@container encart (min-width: 482px) { .wk-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@container encart (min-width: 780px) { .wk-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); } }
.wk-day {
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 8px 9px 9px;
    min-height: 92px;
}
.wk-day.today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.wk-day.past { opacity: .55; }
.wk-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 6px; }
.wk-dow { font-size: 11px; color: var(--muted); text-transform: capitalize; }
.wk-num { margin-left: auto; font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; }
.wk-day.today .wk-num { color: var(--accent); }
.wk-ev {
    display: flex; gap: 5px; align-items: baseline;
    font-size: 11.5px; line-height: 1.35;
    padding: 3px 6px; margin-bottom: 4px;
    border-radius: 6px;
    background: rgba(109, 139, 255, .13);
    border-left: 2px solid var(--accent);
}
.wk-ev.allday { background: rgba(155, 107, 255, .13); border-left-color: var(--accent-2); }
.wk-ev b { font-variant-numeric: tabular-nums; white-space: nowrap; }
.wk-ev span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-empty { margin: 0; color: var(--border); font-size: 12px; }
/* Agenda INTERNE (28/09/2026) : chaque rendez-vous est un lien qui ouvre la modale Agenda
   sur lui (agenda.js) ; sans script, la page /agenda. Barre : ‹ Aujourd'hui › + boutons. */
.wk-barre { margin-bottom: 10px; flex-wrap: wrap; gap: 6px; }
.wk-nav { display: flex; gap: 6px; }
a.wk-ev { color: var(--fg); text-decoration: none; }
a.wk-ev:hover, a.wk-ev:focus-visible { background: rgba(53, 208, 242, .22); outline: none; box-shadow: 0 0 0 1px var(--accent); text-decoration: none; }
a.wk-ev.allday:hover, a.wk-ev.allday:focus-visible { background: rgba(124, 108, 246, .26); box-shadow: 0 0 0 1px var(--accent-2); }
.wk-ev .wk-ev-titre { flex: 1 1 auto; min-width: 0; }
.wk-ev .wk-ev-client { flex: 0 1 auto; font-size: 10px; color: var(--muted); }
@media (max-width: 767px) {
    .dash-agenda .wk-barre .btn { min-height: 44px; }
    .dash-agenda .wk-nav .btn { min-width: 44px; }
    a.wk-ev { min-height: 44px; align-items: center; font-size: 13px; }
}

/* Jauges (heures, facturation) des encarts Klorel et Argent : retirées avec eux (18/10/2026). */

/* --- Listes compactes (tickets) --- */
.mini-list { list-style: none; margin: 0; padding: 0; }
.mini-list li { border-left: 2px solid var(--accent); margin-bottom: 3px; border-radius: 0 6px 6px 0; }
.mini-list li.p3 { border-left-color: var(--danger); }
.mini-list li.p2 { border-left-color: var(--warn); }
.mini-list a {
    display: flex; align-items: baseline; gap: 8px;
    padding: 5px 8px; font-size: 12.5px; color: var(--fg);
    border-radius: 0 6px 6px 0;
}
.mini-list a:hover { background: var(--bg-2); text-decoration: none; }
.ml-site { color: var(--muted); font-size: 11px; white-space: nowrap; max-width: 34%; overflow: hidden; text-overflow: ellipsis; }
.ml-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ml-age { color: var(--muted); font-size: 11px; white-space: nowrap; }
.ml-age.warn { color: var(--warn); }

/* --- Encart « Sécurité » (13/10/2026, App\Service\Securite\SecuritePortee) ---
   Mobile d'abord : groupes empilés, chaque ligne est une cible de 44 px, le détail (chemin d'un
   fichier, signature) passe à la ligne sous le titre au lieu d'élargir la page. Deux colonnes de
   groupes quand l'ENCART dépasse 760 px — il peut être réduit à un tiers en mode Disposer. */
.dash-securite { container: encart / inline-size; }
.sec-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-bottom: 10px; }
.sec-tete h2 { margin: 0; flex: 1 1 auto; min-width: 0; flex-wrap: wrap; overflow-wrap: normal; row-gap: 2px; }
/* Le nom du projet passe à la ligne EN ENTIER (13/10/2026) : coupé lettre par lettre au téléphone
   quand le h2 flex n'avait pas de retour à la ligne (même correctif que l'encart Trafic). */
.sec-tete h2 .wk { flex: 1 1 10em; min-width: 0; overflow-wrap: anywhere; }
.sec-pastilles { display: flex; flex-wrap: wrap; gap: 6px; }
.sec-avert, .sec-cockpit {
    margin: 0 0 8px; padding: 8px 10px; font-size: 12.5px; line-height: 1.45;
    border-radius: 0 6px 6px 0; border-left: 2px solid var(--accent); background: var(--bg-2);
    overflow-wrap: anywhere;
}
.sec-avert.warning, .sec-cockpit.warn { border-left-color: var(--warn); }
.sec-cockpit.bad { border-left-color: var(--danger); }
.sec-cockpit.ok { border-left-color: var(--success); }
.sec-avert a, .sec-cockpit a { color: var(--fg); display: block; }
/* Charte sécurité du cockpit (14/10/2026) : « Charte 60 % · 9 à corriger · 3 à surveiller », couleur de la note. */
.sec-charte {
    margin: 0 0 8px; padding: 0 10px; font-size: 13px; line-height: 1.45;
    border-radius: 0 6px 6px 0; border-left: 3px solid var(--dim); background: var(--bg-2); overflow-wrap: anywhere;
}
.sec-charte a { color: var(--fg); display: flex; align-items: center; min-height: 44px; }
.sec-charte.ok { border-left-color: var(--success); }
.sec-charte.warn { border-left-color: var(--warn); }
.sec-charte.bad { border-left-color: var(--danger); background: rgba(251, 95, 122, .08); }
.sec-charte-sos { color: var(--danger); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
.sec-groupes { display: grid; gap: 4px 20px; grid-template-columns: minmax(0, 1fr); }
@container encart (min-width: 760px) { .sec-groupes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.sec-groupe { min-width: 0; }
.sec-groupe h3 { margin: 12px 0 6px; }
.sec-liste a { flex-wrap: wrap; align-items: center; row-gap: 2px; min-height: 44px; }
.sec-liste a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Le titre passe à la ligne (2 au plus) au lieu d'être coupé : sur téléphone, « Certificat TLS · Expire… » ne disait rien. */
.sec-liste .sec-titre { flex: 1 1 0; white-space: normal; overflow-wrap: anywhere;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.sec-badge { padding: 1px 6px; font-size: 9.5px; }
.sec-detail { flex: 1 0 100%; min-width: 0; color: var(--muted); font-size: 11.5px; line-height: 1.35; overflow-wrap: anywhere;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.sec-plus, .sec-note { margin: 4px 0 0; }
.sec-note { color: var(--muted); }
.sec-liens { display: flex; flex-wrap: wrap; gap: 0 14px; margin: 6px 0 0; }
.sec-liens a, .sec-note a { display: inline-flex; align-items: center; min-height: 44px; }
@media (min-width: 900px) and (pointer: fine) {
    .sec-liste a, .sec-liens a, .sec-note a { min-height: 0; }
}

/* --- Menu « Actions » : Ignorer / Liste blanche (14/10/2026, securite/_actions.html.twig) ---
   Mobile d'abord : le résumé est une cible de 44 × 44 À CÔTÉ du lien de la ligne (jamais dedans),
   posé en haut à droite ; ouvert, le formulaire passe SOUS la ligne, à pleine largeur, sans rien
   élargir. Même menu dans les journaux d'une boutique (agence) et les alertes. */
.sec-liste li.sec-ligne { position: relative; display: flex; flex-wrap: wrap; min-height: 44px; padding-right: 44px; }
.sec-liste li.sec-ligne > a { flex: 1 1 100%; min-width: 0; }
.sec-liste li.sec-ligne > .sec-act { flex: 1 1 100%; min-width: 0; margin-right: -44px; }
.sec-liste li.sec-ligne > .sec-act > summary { position: absolute; top: 0; right: 0; }
.sec-act > summary { list-style: none; cursor: pointer; }
.sec-act > summary::-webkit-details-marker { display: none; }
.sec-act-bouton { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px;
    color: var(--muted); font-size: 20px; line-height: 1; border-radius: 6px; }
.sec-act-bouton:hover, .sec-act[open] > .sec-act-bouton { color: var(--accent); background: var(--bg-2); }
.sec-act > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.sec-act-texte { display: inline-flex; align-items: center; min-height: 44px; color: var(--accent); font-size: 12.5px; }
.sec-act-form { margin: 4px 0 8px; padding: 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-2);
    display: grid; gap: 10px; font-size: 12.5px; overflow-wrap: anywhere; }
.sec-act-choix { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
.sec-act-choix > select, .sec-act-choix > input[type=text] { flex: 1 1 12em; min-width: 0; min-height: 44px; font-size: 16px; }
.sec-act-label { flex: 1 0 100%; color: var(--muted); font-size: 11.5px; }
.sec-act-confirme { flex: 1 0 100%; display: flex; align-items: center; gap: 8px; min-height: 44px; }
.sec-act-go { min-height: 44px; }
.sec-act-aide { flex: 1 0 100%; margin: 0; color: var(--muted); font-size: 11.5px; line-height: 1.4; }
.sec-act-interdit { color: var(--warn); }
.sec-act-effet { margin: 0; padding-top: 8px; border-top: 1px solid var(--border); color: var(--muted); font-size: 11.5px; }
.sec-flash { margin: 0 0 8px; padding: 8px 10px; font-size: 12.5px; border-radius: 0 6px 6px 0; border-left: 2px solid var(--success); background: var(--bg-2); }
.sec-flash.bad { border-left-color: var(--danger); }
.sec-ignores { margin: 4px 0 0; }
.sec-ignores a { display: inline-flex; align-items: center; min-height: 44px; color: var(--muted); }
/* Journaux d'une boutique (agence) : colonne d'actions, lignes ignorées grisées (?ignores=1). */
.jr-act { width: 1%; white-space: nowrap; }
.jr-act .sec-act[open] { white-space: normal; min-width: min(20rem, calc(100vw - 64px)); }
.jr-ignore > td:not(.jr-act) { opacity: .55; }
.jr-reactiver { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; white-space: normal; }
.jr-reactiver .btn, .jr-ignores { min-height: 44px; display: inline-flex; align-items: center; }
.al-blanche { flex: 1 0 100%; min-width: 0; }
.al-blanche form:not(.sec-act-form) { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
/* Écran des décisions (/securite/exceptions) : une carte par décision, pas de tableau. */
.secx-liste { list-style: none; margin: 0; padding: 0; }
.secx-ligne { padding: 10px 0; border-top: 1px solid var(--border); min-width: 0; overflow-wrap: anywhere; }
.secx-ligne:first-child { border-top: 0; }
.secx-inactive { opacity: .6; }
.secx-tete { display: flex; flex-wrap: wrap; gap: 6px; }
.secx-libelle { margin: 6px 0 2px; font-weight: 600; }
.secx-detail, .secx-meta, .secx-raison, .secx-sync { margin: 2px 0; }
.secx-reactiver .btn, .secx-filtres .btn { min-height: 44px; display: inline-flex; align-items: center; }
@media (min-width: 900px) and (pointer: fine) {
    .sec-act-bouton { width: 36px; height: 32px; }
    .sec-liste li.sec-ligne { min-height: 0; padding-right: 36px; }
    .sec-liste li.sec-ligne > .sec-act { margin-right: -36px; }
    .sec-act-choix > select, .sec-act-choix > input[type=text], .sec-act-go, .sec-act-confirme, .sec-ignores a,
    .jr-reactiver .btn, .jr-ignores, .secx-reactiver .btn, .secx-filtres .btn { min-height: 0; }
    .sec-act-choix > select, .sec-act-choix > input[type=text] { font-size: inherit; }
}

/* --- Pastilles de lien (modules, audit) --- */
/* Les règles .cl-* de l'encart « Clients » ont été retirées le 22/09/2026 avec
   l'encart lui-même : il faisait doublon avec le parc. .pill reste — elle sert
   encore à l'écran Modules et au PDF d'audit. */
.pill {
    display: inline-block; padding: 1px 8px; border-radius: 10px;
    border: 1px solid var(--border); background: var(--bg-2);
    font-size: 11px; color: var(--fg); white-space: nowrap;
}
.pill:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.pill.bo { border-color: rgba(155, 107, 255, .5); color: var(--accent-2); }
.pill.ghost { color: var(--muted); border-style: dashed; }

/* Filtre de la liste des tickets : le champ vit dans une barre d'outils en flex,
   il lui faut une largeur propre sinon il se réduit à son contenu. */
.tk-filtre-combo { min-width: 240px; flex: 0 1 300px; }
@media (max-width: 620px) { .tk-filtre-combo { flex: 1 1 100%; } }

/* --- Champ à auto-complétion (choix d'une boutique au clavier) --- */
/* Remplace un <select> de quinze entrées dont les noms ne commencent pas par ce
   qu'on tape. Tout se fait au clavier : frappe, flèches, Entrée. */
.kl-combo { position: relative; }
.kl-combo input[aria-invalid="true"] { border-color: var(--danger); }

.kl-combo-liste {
  position: absolute; z-index: 30; left: 0; right: 0; top: 100%;
  margin: 4px 0 0; padding: 4px; list-style: none;
  max-height: 240px; overflow-y: auto;
  background: var(--card-2); border: 1px solid var(--border-lit);
  border-radius: var(--radius-sm); box-shadow: var(--lift);
}
/* `:not([hidden])` inutile ici : la liste n'a pas de display imposé, l'attribut
   hidden suffit. On le note pour ne pas « corriger » ce qui marche. */
.kl-combo-liste li {
  padding: 7px 10px; border-radius: 6px; cursor: pointer;
  font-size: 13px; color: var(--fg); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* Un seul état visuel pour le survol ET la sélection au clavier : sans cela, on
   ne sait plus laquelle des deux lignes Entrée va valider. */
.kl-combo-liste li.on,
.kl-combo-liste li:hover { background: var(--accent-soft); color: var(--accent); }
.kl-combo-liste li.vide { color: var(--muted); cursor: default; font-style: italic; }
/* Proposition avec complément (projet · date) : le texte s'abrège, le complément reste entier. */
.kl-combo-liste li.avec-detail { display: flex; align-items: baseline; gap: 10px; }
.kl-combo-liste .kl-combo-texte { flex: 1 1 auto; min-width: 30%; overflow: hidden; text-overflow: ellipsis; }
.kl-combo-liste .kl-combo-detail { flex: 0 1 auto; max-width: 64%; overflow: hidden; text-overflow: ellipsis; font-size: 11.5px; color: var(--muted); }
.kl-combo-liste li.vide:hover { background: none; color: var(--muted); }

/* Rappel du raccourci d'envoi, à côté du bouton. */
/* Rappel du raccourci : le style `kbd` global (l. 1079) fait deja le travail. */
.tq-astuce { margin-left: 10px; font-size: 11px; color: var(--dim); }

/* Utilisateurs associés dès la création du ticket (07/10/2026) : mêmes pastilles que sous l'en-tête d'un ticket. */
.tq-associes[hidden] { display: none; }
.tq-associes-liste { display: flex; flex-wrap: wrap; gap: 8px; }
.tq-as {
  display: inline-flex; align-items: center; gap: 7px; min-height: 32px; max-width: 240px; padding: 5px 12px 5px 9px;
  border-radius: 999px; border: 1px solid var(--border); background: var(--card-2); color: var(--muted);
  font: inherit; font-size: 12.5px; line-height: 1.2; cursor: pointer; transition: border-color .15s, background .15s, color .15s;
}
.tq-as:hover { border-color: var(--border-lit); color: var(--fg); }
.tq-as:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tq-as-nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tq-as-coche { flex: none; width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid currentColor; opacity: .55; position: relative; }
.tq-as[aria-pressed="true"] { color: var(--fg); border-color: rgba(53, 208, 242, .55); background: linear-gradient(135deg, rgba(53, 208, 242, .18), rgba(124, 92, 255, .16)); }
.tq-as[aria-pressed="true"] .tq-as-coche { opacity: 1; border-color: transparent; background: linear-gradient(135deg, #35d0f2, #7c5cff); }
.tq-as[aria-pressed="true"] .tq-as-coche::after { content: ''; position: absolute; left: 4.5px; top: 2px; width: 3px; height: 6px; border: solid #0b1324; border-width: 0 1.8px 1.8px 0; transform: rotate(45deg); }
.tq-as-mail { font-size: 12px; color: var(--accent); }
.tq-as.sans-mail .tq-as-nom::after { content: ' · sans e-mail'; font-size: 11px; color: var(--muted); }
@media (hover: none) { .tq-as { min-height: 40px; } }

/* « @ » dans la description du nouveau ticket (07/10/2026) : même habit que la liste de la discussion. */
.tq-desc-champ { position: relative; }
.tq-mentions {
  position: absolute; left: 0; right: 0; z-index: 30; margin: 4px 0 0; padding: 4px; list-style: none;
  max-width: 360px; max-height: 240px; overflow-y: auto;
  background: var(--card-2); border: 1px solid var(--border-lit); border-radius: 10px; box-shadow: var(--lift);
}
.tq-mentions[hidden] { display: none; }
.tq-mentions li { padding: 8px 10px; border-radius: 6px; cursor: pointer; font-size: 13.5px; }
.tq-mentions li[aria-selected="true"], .tq-mentions li:hover { background: rgba(53, 208, 242, .16); color: var(--accent); }
@media (hover: none) { .tq-mentions li { min-height: 44px; display: flex; align-items: center; } }

/* Pièces jointes du nouveau ticket : glisser-déposer, sélecteur, capture collée (05/10/2026).
   Le champ natif reste dans la zone, invisible mais atteignable au clavier : c'est lui qu'on
   envoie, et la zone (son <label>) montre le focus. */
/* L'intitulé reprend l'habit des <label> (l. 153 et 163) ; la zone, elle, est un <label> qui s'en défait. */
.tq-label { display: block; margin: 0 0 5px; font-size: 10.5px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.tq-label .muted { text-transform: none; letter-spacing: normal; font-weight: 400; }
.tq-depot {
  position: relative; display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 4px;
  min-height: 64px; padding: 12px 14px; margin: 0;
  border: 2px dashed var(--border); border-radius: var(--radius); background: rgba(255, 255, 255, .02);
  color: var(--muted); font-size: 13px; font-weight: 400; letter-spacing: normal; text-transform: none;
  text-align: center; cursor: pointer;
  transition: border-color .15s, background .15s, color .15s;
}
.tq-depot:hover, .tq-depot:focus-within { border-color: var(--accent); color: var(--fg); }
.tq-depot b { color: var(--accent); font-weight: 600; }
.tq-depot-champ { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); border: 0; opacity: 0; }
.tq-depot-lacher { display: none; font-weight: 600; }
/* Les textes de la zone ne prennent jamais le pointeur : ils changent quand des fichiers la
   survolent, et un élément remplacé sous le curseur en plein glisser relance dragenter/dragleave
   (la zone clignotait et le dépôt pouvait être refusé). La cible reste la zone elle-même. */
.tq-depot > * { pointer-events: none; }
/* Des fichiers survolent la fenêtre : tout le formulaire s'allume, la zone dit « Lâchez ». */
.tk-quick.tq-survol { outline: 2px dashed var(--accent); outline-offset: 6px; border-radius: var(--radius); }
.tk-quick.tq-survol .tq-depot { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.tk-quick.tq-survol .tq-depot-texte { display: none; }
.tk-quick.tq-survol .tq-depot-lacher { display: inline; }
.tq-fichiers { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.tq-fichiers[hidden] { display: none; }
.tq-fichiers li {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  padding: 3px 4px 3px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 12.5px;
}
.tq-f-nom { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tq-f-taille { flex: none; color: var(--muted); font-family: var(--font-num); font-size: 11px; }
.tq-f-x { flex: none; width: 28px; height: 28px; padding: 0; border: 0; border-radius: 4px; background: none; color: var(--muted); font-size: 17px; line-height: 1; cursor: pointer; }
.tq-f-x:hover, .tq-f-x:focus-visible { color: var(--danger); background: rgba(251, 95, 122, .1); }
.tq-fichiers .tq-f-refus { padding: 6px 10px; border-color: rgba(251, 95, 122, .45); color: var(--danger); }
.tq-depot-doigt { display: none; }
/* Au doigt, rien ne se glisse : la zone dit « Ajouter des fichiers », la croix grandit. */
@media (hover: none) {
  .tq-depot-ordi { display: none; }
  .tq-depot-doigt { display: inline; }
  .tq-f-x { width: 44px; height: 44px; }
}

/* --- Liste des clients : filtre en direct + navigation au clavier --- */
.cl-filtre { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 12px; }
.cl-filtre input { flex: 1 1 280px; max-width: 460px; }

/* --- Écran Modules : où est installé un module, activé, en quelle version ---
   Mobile-first : une boutique = trois blocs empilés (boutique + état, versions,
   fraîcheur + back-office), en grille de trois colonnes à partir de 600 px. Pas de
   tableau à défilement horizontal : l'écran sert au pouce, en boutique. */
.mod-recherche { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 0 0 6px; }
.mod-combo { flex: 1 1 200px; max-width: 520px; }
.mod-combo input { width: 100%; }
.mod-couverture { margin: 0 0 14px; }
.mod-fraicheur { margin: -8px 0 14px; color: var(--accent); }
.mod-vieux { color: var(--warn); }

.mod-entete { margin-bottom: 10px; }
.mod-titre { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* Un nom technique garde sa casse : en capitales, « ps_facetedsearch » ne se lirait plus. */
.mod-nom { margin: 0; text-transform: none; letter-spacing: 0; font-family: var(--font-num); font-size: 17px; overflow-wrap: anywhere; }
.mod-display { margin: 2px 0 0; }
.mod-fiche:focus { outline: none; }
.mod-fiche:focus-visible { border-color: var(--border-lit); }
.mod-resume { margin: 0 0 12px; font-size: 13px; line-height: 1.8; color: var(--muted); }
.mod-resume strong { color: var(--fg); }
.mod-sep { margin: 0 6px; color: var(--dim); }
.mod-sous-titre { margin: 16px 0 2px; }

.mod-lignes { list-style: none; margin: 0; padding: 0; }
.mod-ligne { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; padding: 11px 0; border-top: 1px solid var(--border); }
.mod-ligne:first-child { border-top: 0; }
.mod-ligne > div { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
.mod-ligne strong { overflow-wrap: anywhere; }
/* Libellés d'état longs (« activation non fiable… ») : ils passent à la ligne au lieu de déborder à 375 px. */
.mod-ligne .badge { white-space: normal; }
.mod-ligne.grise { opacity: .55; }
.mod-action { flex-basis: 100%; }
.mod-v { display: inline-flex; align-items: center; gap: 5px; }
.mod-etiquette { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .09em; color: var(--dim); }
.mod-lien { color: inherit; }
.mod-lien:hover { text-decoration: none; }
.mod-lien:hover strong { color: var(--accent); }
.mod-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
@media (min-width: 600px) {
  .mod-ligne { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
  .mod-releve { justify-content: flex-end; }
}

.mod-replie summary { cursor: pointer; color: var(--muted); font-size: 12.5px; }
.mod-replie summary:hover { color: var(--accent); }
.mod-replie:not(.card) { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.mod-replie[open] summary { margin-bottom: 4px; }

.mod-resultats { list-style: none; margin: 0; padding: 0; }
.mod-resultat { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; padding: 10px 0; border-top: 1px solid var(--border); }
.mod-resultat:first-child { border-top: 0; }
.mod-resultat .mod-lien { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; min-width: 0; }
.mod-resultat .mod-display { margin: 0; color: var(--muted); font-size: 12.5px; }
.mod-compteurs { flex-basis: 100%; }
@media (min-width: 600px) { .mod-compteurs { flex-basis: auto; margin-left: auto; } }

.mod-vide ol { margin: 0 0 14px; padding-left: 20px; }
.mod-vide li { margin-bottom: 6px; }

/* Export du module d'une boutique (fiche d'un module) : un encart sous la ligne, sur toute
   la largeur de la grille. Mobile d'abord : boutons et phrases passent à la ligne ; les
   chemins suspects (sans espace) se coupent n'importe où plutôt que de déborder. */
.mod-export-note { margin: -4px 0 12px; }
.mod-ligne > .mod-export { display: block; grid-column: 1 / -1; min-width: 0; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: rgba(18, 26, 43, .55); }
.mod-export:focus { outline: none; }
.mod-export:focus-visible { border-color: var(--border-lit); box-shadow: 0 0 0 1px var(--border-lit); }
.mod-export p { margin: 0 0 8px; overflow-wrap: anywhere; }
.mod-export > :last-child { margin-bottom: 0; }
.mod-export-etat { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 13px; }
.mod-ligne .mod-export-etat .badge { white-space: nowrap; }
.mod-export-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; min-width: 0; }
.mod-export-actions form { margin: 0; }
.mod-export-raison { flex: 1 1 220px; min-width: 0; }
.mod-export-suspects { margin: 0 0 8px; }
.mod-export-liste { margin: 4px 0 8px; padding-left: 18px; font-family: var(--font-num); font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }
.mod-export-plus summary { cursor: pointer; color: var(--muted); font-size: 12.5px; }
.mod-export-plus summary:hover { color: var(--accent); }
.mod-export-licence { color: var(--warn); }
.mod-export-nouveau { color: var(--accent); font-weight: 600; }
@media (pointer: coarse) {
  .mod-export-actions .btn, .mod-export-plus summary { min-height: 44px; }
  .mod-export-rafraichir { display: inline-flex; align-items: center; min-height: 44px; }
}

/* --- Lignes cliquables (tr.rowlink) : un vrai lien par ligne ---
   Le nom est un lien (un arrêt de tabulation, empilé dans la modale) ; un clic
   ailleurs sur la ligne le suit, en JavaScript (_modal.html.twig).
   PAS de lien « étiré » en ::after absolu : Safari ne fait pas d'un <tr> le repère
   des positions absolues, et le ::after de la dernière ligne recouvrait toute la
   modale sur iPhone (chaque appui ouvrait le même client). */
.rowlink-cible { color: inherit; }
.rowlink-cible:hover { text-decoration: none; color: var(--accent); }
.rowlink-cible:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
/* La ligne entière signale le focus clavier, comme au survol. */
tr.rowlink:has(.rowlink-cible:focus-visible) td { background: rgba(53, 208, 242, .07); }
tr.rowlink:has(.rowlink-cible:focus-visible) td:first-child { box-shadow: inset 2px 0 0 var(--accent); }

/* --- Dépôt de fichiers sur un ticket --- */
/* Le voile couvre le corps de la modale, qui est la surface de dépôt. En
   `position: fixed` et non `absolute` : le corps de modale défile, un voile
   absolu se serait décalé avec le contenu dès que le ticket est long. */
.pj-voile {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  background: rgba(8, 11, 19, .82);
  backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  /* Ne JAMAIS intercepter le pointeur : le voile est sous le curseur au moment
     du lâcher, et c'est la zone en dessous qui doit recevoir l'événement. */
  pointer-events: none;
}
.pj-voile span {
  padding: 16px 26px; border-radius: var(--radius);
  border: 2px dashed var(--accent); background: var(--accent-soft);
  color: var(--accent); font: 600 14px var(--font); letter-spacing: .02em;
}

/* --- Mentions dans un commentaire de ticket --- */
.tk-mention { position: relative; }
.tk-puces { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.tk-puces:empty { margin-top: 0; }
.tk-puce {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px; border-radius: 999px;
  background: var(--accent-soft); border: 1px solid rgba(53, 208, 242, .38);
  color: var(--accent); font: 600 11.5px var(--font); white-space: nowrap;
}
/* Sur un commentaire déjà envoyé, la puce n'est qu'une trace : ni bouton, ni
   couleur d'action. */
.tk-puces.lu .tk-puce {
  background: rgba(255, 255, 255, .04); border-color: var(--border); color: var(--muted);
}
.tk-puce-x {
  background: none; border: 0; padding: 0 0 0 2px; cursor: pointer;
  color: inherit; font: 700 14px/1 var(--font); opacity: .7;
}
.tk-puce-x:hover { opacity: 1; }

/* Encart « Raccourcis » (.dash-liens, .lien-*, .liens-*) : retiré le 18/10/2026, bouton « Accès
   rapide » de l'en-tête (public/css/entete.css). */

/* --- Parc : une ligne par boutique, regroupée par client --- */
/* Le TEMPS est toujours celui de la boutique de la ligne : jamais fusionné.
   Les colonnes d'ARGENT, elles, suivent le réglage de facturation du client
   (Client::factureParSite) : « une facture par site » -> un chiffre par ligne ;
   « une facture pour tous les sites » -> cellule fusionnee (rowspan), parce que
   repeter le chiffre ferait compter cinq fois le client qui a cinq boutiques.
   La fusion dit visuellement « ce chiffre vaut pour ce groupe », et la colonne
   reste additionnable dans les deux cas. */
.parc-tbl { width: 100%; }
.parc-tbl td { vertical-align: middle; }
/* Filet entre deux clients : sans lui, un groupe de cinq lignes se confond avec
   cinq clients differents. */
.parc-tbl tbody tr.grp-debut > td { border-top: 1px solid var(--border-lit); }
.parc-tbl tbody tr:first-child > td { border-top: 0; }

/* Cellule fusionnee : fond legerement creuse pour se lire comme un bloc. */
.parc-tbl td.grp { background: rgba(255, 255, 255, .022); }
.parc-tbl td.fort { font-family: var(--font-num); font-size: 14px; color: var(--fg); font-weight: 700; }
.parc-tbl .du { font-family: var(--font-num); color: var(--warn); font-weight: 700; }

.parc-nom { display: inline-flex; align-items: center; gap: 7px; }
.parc-nom:hover { text-decoration: none; }
.parc-nom strong { color: var(--fg); }
.parc-nom:hover strong { color: var(--accent); }
/* ⌖ contextualiser le tableau de bord sur ce projet (28/09/2026) : discret, visible au survol de la ligne. */
.parc-ctx { display: inline; margin: 0 0 0 4px; }
.parc-ctx-btn { background: none; border: 0; padding: 2px; margin: 0; cursor: pointer; color: var(--muted); opacity: .55; vertical-align: middle; line-height: 0; border-radius: 5px; }
.parc-ctx-btn .ic { width: 14px; height: 14px; }
.parc-tbl tr:hover .parc-ctx-btn, .parc-ctx-btn:focus-visible { opacity: 1; }
.parc-ctx-btn:hover { color: var(--accent); background: rgba(53, 208, 242, .1); }
@media (max-width: 767px), (pointer: coarse) { .parc-ctx-btn { padding: 8px; margin: -6px 0 -6px 4px; opacity: .8; } }

/* FO / BO : deux pastilles courtes, lisibles au pouce sur telephone. */
.parc-tbl td.parc-ver { font-family: var(--font-num); white-space: nowrap; }
/* Admin Cockpit en retard sur la dernière version publiée : ROUGE depuis le 29/09/2026
   (vert = dernière version, rouge = toute autre). */
.parc-retard { color: var(--danger); border-bottom: 1px dashed rgba(251, 95, 122, .55); }
.parc-retard:hover { color: var(--danger); text-decoration: none; border-bottom-style: solid; }
/* Couleur des versions PrestaShop / PHP / Admin Cockpit (EtatVersions, 29/09/2026) :
   ok = à jour, warn = à surveiller, bad = dépassée ; na = inconnue, sans couleur. */
.parc-v-ok { color: var(--success); }
.parc-v-warn { color: var(--warn); }
.parc-v-bad { color: var(--danger); }
span.parc-v-ok, span.parc-v-warn, span.parc-v-bad { cursor: help; }
/* Liste des clients : version d'Admin Cockpit derrière le nom du site. */
.cl-cockpit { font-family: var(--font-num); font-size: 11px; color: var(--muted); white-space: nowrap; }
/* Module tiers (Security Pro) au parc : une donnée d'abord, aucune couleur au repos
   sauf ce qui demande une action (retard, fichiers seuls). Classe DISTINCTE de
   .parc-retard, que le banc cockpit-version compte pour Admin Cockpit seul. */
.parc-mod { color: inherit; }
.parc-mod:hover { color: var(--accent); text-decoration: none; }
.parc-mod-retard { color: var(--warn); border-bottom: 1px dashed rgba(251, 191, 36, .55); }
.parc-mod-retard:hover { color: var(--warn); border-bottom-style: solid; }
.parc-mod-warn { color: var(--warn); }
.parc-mod-off, .parc-mod-absent, .parc-mod-inconnu, .parc-mod-grise { color: var(--muted); }
.parc-mod-vieux { font-style: italic; }
/* Écran Clients : Security Pro derrière la version d'Admin Cockpit. */
.cl-secpro { font-family: var(--font-num); font-size: 11px; color: var(--muted); white-space: nowrap; }
.cl-secpro-retard, .cl-secpro-warn { color: var(--warn); }
/* PageSpeed au parc (lot A, 26/09/2026) : note Performance mobile · ordinateur,
   mesurée par DELMAK, aux couleurs de Google (90 / 50). Classe parc-psi et JAMAIS
   parc-ver : le banc cockpit-version compte les parc-ver par index. */
.parc-tbl td.parc-psi { white-space: nowrap; font-family: var(--font-num); }
/* Uptime 7 jours (28/09/2026) : pourcentage + mini-courbe, le tout cliquable → fiche. */
.parc-tbl td.parc-uptime { white-space: nowrap; min-width: 118px; }
.parc-uptime-lien { display: inline-flex; align-items: center; gap: 7px; text-decoration: none; }
.parc-uptime-lien:hover { text-decoration: none; }
.parc-uptime-lien b { font-family: var(--font-num); font-size: 12px; min-width: 52px; }
.parc-uptime .spark { width: 64px; min-width: 64px; height: 20px; }
.parc-uptime .spark-bornes { display: none; }
.parc-uptime .spark-vide { font-size: 11px; }
.parc-uptime b.ok { color: var(--success); }
.parc-uptime b.warn { color: var(--warn); }
.parc-uptime b.bad { color: var(--danger); }
.parc-uptime b.vide { color: var(--muted); }
/* Dernier audit : date cliquable → aperçu des audits ; rapport non publié en italique. */
.parc-tbl td.parc-audit { white-space: nowrap; font-family: var(--font-num); }
/* Utilisateurs (07/10/2026) : nombre de comptes de l'espace du client, lien vers la fenêtre Utilisateurs.
   Cible tactile pleine cellule ; 0 grisé (rien à gérer, mais on peut créer le premier accès). */
.parc-tbl td.parc-users { text-align: center; font-family: var(--font-num); }
.parc-tbl th.parc-users-th { text-align: center; }
.parc-users-th .ic { width: 16px; height: 16px; vertical-align: -3px; }
.parc-users .chiffre-lien { display: inline-flex; align-items: center; justify-content: center; min-width: 32px; min-height: 32px; }
.parc-users .parc-users-vide { opacity: .45; }
@media (hover: none) { .parc-users .chiffre-lien { min-width: 44px; min-height: 44px; } }
.parc-audit .parc-audit-brouillon { font-style: italic; opacity: .75; }
/* Fraîcheur du dernier audit (03/10/2026) : orange au-delà d'une semaine, rouge au-delà d'un mois.
   Spécificité 0,2,0 : passe devant .chiffre-lien { color: inherit } ; le survol garde l'accent. */
.parc-audit .parc-audit-semaine { color: var(--orange); }
.parc-audit .parc-audit-mois { color: var(--danger); }
.parc-audit .parc-audit-semaine:hover, .parc-audit .parc-audit-mois:hover { color: var(--accent); }
/* Aucun audit (23/10/2026) : « + » discret qui ouvre le volet sur « Nouvel audit » ; cible tactile de 44 px au doigt. */
.parc-audit .parc-audit-ajout { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; min-height: 28px; color: var(--muted); font-weight: 600; }
.parc-audit .parc-audit-ajout:hover { color: var(--accent); text-decoration: none; }
@media (hover: none) { .parc-audit .parc-audit-ajout { min-width: 44px; min-height: 44px; } }
.psi-ok { color: var(--success); } .psi-warn { color: var(--warn); } .psi-bad { color: var(--danger); }
.psi-na { color: var(--muted); }
.psi-sep { color: var(--muted); }
.psi-delta { font-size: 10.5px; margin-left: 2px; }
/* Note de plus de 48 h, ou dernière analyse en échec : elle n'est plus celle du jour. */
.psi-perime { opacity: .55; }
/* Spécificité 0,2,0 : .chiffre-lien (plus bas) remet la couleur héritée. */
.parc-psi .psi-vide { color: var(--muted); }
.parc-psi .psi-echec-lien { color: var(--warn); }
.psi-sans-cle { color: var(--warn); }
/* Écran Clients : note mobile derrière Security Pro. */
.cl-psi { font-family: var(--font-num); font-size: 11px; white-space: nowrap; }
.cl-psi:hover { text-decoration: none; filter: brightness(1.15); }

/* Écran de détail PageSpeed (modale) : cartes empilées, une colonne sur téléphone. */
.psi-detail .psi-appareil h2 { margin-bottom: 10px; }
.psi-detail .psi-cats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.psi-detail .psi-cat {
  text-align: center; min-width: 0; padding: 8px 4px;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.psi-detail .psi-cat .psi { display: block; font-family: var(--font-num); font-size: 22px; font-weight: 700; line-height: 1.2; }
.psi-detail .psi-cat > span:last-child { display: block; font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; overflow-wrap: anywhere; }
@media (max-width: 379px) { .psi-detail .psi-cats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.psi-detail .psi-appareil-note { margin: 10px 0 0; }
.psi-detail .psi-dernier-echec { margin: 8px 0 0; overflow-wrap: anywhere; }
.psi-echec { color: var(--warn); }
.psi-echec code { font-family: var(--font-num); font-size: 11.5px; color: inherit; }
.psi-echec-detail { display: block; color: var(--muted); font-size: 12px; }
.psi-detail .psi-tbl td { padding-top: 8px; padding-bottom: 8px; }
/* Une valeur ne se coupe jamais (« 410 / ms » sur deux lignes à 375 px) : le tableau défile. */
.psi-detail .psi-tbl td.num, .psi-detail .psi-tbl td.psi-jour { white-space: nowrap; }
.psi-detail .psi-tbl td.psi-jour { font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.psi-detail .psi-statut { white-space: nowrap; }
.psi-detail .psi-statut-ligne { display: block; }
.psi-detail .psi-terrain { display: grid; gap: 10px; margin: 0; grid-template-columns: 1fr; }
@media (min-width: 640px) { .psi-detail .psi-terrain { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.psi-detail .psi-terrain dt { font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.psi-detail .psi-terrain dd { margin: 2px 0 0; font-family: var(--font-num); font-size: 16px; font-weight: 700; }
.psi-detail .psi-courbe-titre { margin: 14px 0 4px; }
.psi-detail .psi-courbe-titre:first-of-type { margin-top: 0; }
.psi-detail .psi-autres-courbes > summary { display: flex; align-items: center; min-height: 44px; cursor: pointer; color: var(--muted); font-size: 12.5px; }
.psi-detail .psi-pistes { list-style: none; margin: 0; padding: 0; }
.psi-detail .psi-pistes li { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-top: 1px solid var(--border); }
.psi-detail .psi-pistes li:first-child { border-top: 0; }
.psi-gain { font-family: var(--font-num); color: var(--warn); white-space: nowrap; }
/* Courbes : gabarit de l'espace (espace/_perf_courbe.html.twig), stylé ici sans charger
   espace/performances.css dans l'admin. */
.psi-detail .esp-pf-courbe { display: block; width: 100%; height: 90px; overflow: visible; margin-top: 2px; }
.psi-detail .psi-autres-courbes .esp-pf-courbe { height: 60px; }
.psi-detail .esp-pf-trace { stroke: var(--accent); }
.psi-detail .esp-pf-repere { stroke: var(--border-lit); stroke-width: 1; stroke-dasharray: 3 4; }
.psi-detail .esp-pf-courbe-vide { margin: 4px 0 0; color: var(--muted); font-size: 12.5px; }
.psi-detail .esp-pf-courbe-legende { display: flex; justify-content: space-between; gap: 8px; margin: 4px 0 0; color: var(--muted); font-size: 12px; font-family: var(--font-num); }
/* Étape 2 : « Analyser maintenant », analyse en cours, rapports du jour (PDF, JSON). */
.psi-detail .psi-en-cours { color: var(--accent); font-size: 12.5px; font-weight: 600; }
.psi-detail .psi-rapports { white-space: nowrap; }
.psi-detail .psi-rapports a {
  display: inline-flex; align-items: center; min-height: 30px; padding: 0 8px; margin-right: 4px;
  border: 1px solid var(--border); border-radius: 5px; font-size: 11px; font-weight: 600; letter-spacing: .04em;
}
.psi-detail .psi-rapports a:hover { border-color: var(--accent); text-decoration: none; }
/* Doigt sur téléphone : cible de 44 px. */
@media (pointer: coarse) { .psi-detail .psi-rapports a { min-height: 44px; padding: 0 12px; } }
.psi-detail .psi-archive { display: block; margin-top: 4px; font-family: var(--font-num); }

/* PageSpeed sur TROIS pages (17/10/2026) : une carte par page (accueil, catégorie, produit), sur la
   fiche du site et en tête de l'écran de détail. Mobile d'abord : empilées, l'URL coupée à la
   largeur ; trois colonnes à partir de 900 px. Les onglets de l'écran de détail suivent. */
.psi-pages-tete { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; }
.psi-pages-tete h2 { margin: 0; flex: 1 1 auto; min-width: 0; }
.psi-pages-intro { margin: 4px 0 12px; }
.psi-pages-liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) { .psi-pages-liste { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.psi-page {
  min-width: 0; padding: 10px 12px; display: flex; flex-direction: column; gap: 6px;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.psi-page-courante { border-color: var(--accent); }
.psi-page-nom { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.psi-page-voir { font-size: 12px; white-space: nowrap; }
.psi-page-url { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-num); font-size: 11.5px; }
.psi-page-notes { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 12.5px; color: var(--muted); }
.psi-page-notes .psi { font-family: var(--font-num); font-size: 18px; font-weight: 700; margin-left: 4px; color: inherit; }
.psi-page-notes .psi.psi-ok { color: var(--success); } .psi-page-notes .psi.psi-warn { color: var(--warn); } .psi-page-notes .psi.psi-bad { color: var(--danger); } .psi-page-notes .psi.psi-na { color: var(--muted); }
.psi-page-cwv { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin: 0; }
.psi-page-cwv > div { min-width: 0; }
.psi-page-cwv dt { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.psi-page-cwv dd { margin: 1px 0 0; font-family: var(--font-num); font-size: 13px; font-weight: 700; white-space: nowrap; }
.psi-page-pied, .psi-page-raison, .psi-page-liens { margin: 0; overflow-wrap: anywhere; }
.psi-page-raison { color: var(--warn); }
.psi-page-liens { display: flex; flex-wrap: wrap; gap: 4px 10px; }
.psi-page-liens a { display: inline-flex; align-items: center; min-height: 28px; }
@media (pointer: coarse) { .psi-page-liens a { min-height: 44px; } }
.psi-pages-form { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.psi-pages-form h3 { margin: 0 0 4px; }
.psi-pages-saisie { display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 720px) { .psi-pages-saisie { grid-template-columns: repeat(2, minmax(0, 1fr)); } .psi-pages-saisie .help, .psi-pages-saisie .psi-pages-boutons { grid-column: 1 / -1; } }
.psi-pages-saisie .field { margin: 0; min-width: 0; }
.psi-pages-saisie input { width: 100%; min-width: 0; }
.psi-pages-boutons { display: flex; flex-wrap: wrap; gap: 8px; }
.psi-pages-decouvrir { margin: 0; }
.psi-pages-corriger { margin: 10px 0 0; }
.psi-onglets { display: flex; gap: 6px; margin: 0 0 14px; overflow-x: auto; }
.psi-onglet {
  display: inline-flex; align-items: center; min-height: 36px; padding: 0 14px; white-space: nowrap;
  border: 1px solid var(--border); border-radius: 999px; font-size: 12.5px; font-weight: 600; color: var(--muted);
}
.psi-onglet:hover { text-decoration: none; border-color: var(--border-lit); color: var(--fg); }
.psi-onglet.actif { border-color: var(--accent); color: var(--accent); }
@media (pointer: coarse) { .psi-onglet { min-height: 44px; } }

.parc-liens { white-space: nowrap; }
.parc-liens a {
  display: inline-block; min-width: 30px; text-align: center;
  padding: 3px 7px; margin-right: 4px; border-radius: 5px;
  border: 1px solid var(--border); background: rgba(255, 255, 255, .03);
  font: 600 10.5px var(--font); letter-spacing: .06em; color: var(--muted);
}
.parc-liens a:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
/* Back-office non renseigné : le lien mène à la fiche pour le poser. En pointillé,
   parce que ce n'est pas un accès mais une chose à faire (reprise de l'ancien
   encart « Clients », retiré du tableau de bord le 22/09/2026). */
.parc-liens a.parc-bo-vide { border-style: dashed; opacity: .75; }

/* Mention du MODE DE FACTURATION, sous le nom du client. Discrète — c'est elle qui
   explique pourquoi les colonnes d'argent sont fusionnées, ou ne le sont pas. */
.grp-mode {
  display: block; margin-top: 2px;
  font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted);
}
.grp-mode.par-site { color: var(--accent-2); }

/* Ligne « sans boutique » d'un groupe : du temps ou une facture qu'aucune boutique
   ne revendique. Elle doit se lire comme une RÉSERVE, pas comme une boutique de
   plus — mais surtout pas disparaître : c'est de l'argent. */
.parc-tbl tr.parc-sans-site > td { background: rgba(251, 191, 36, .045); }
.parc-orphelin { font-style: italic; }

/* Ligne de totaux : c'est la lecture de pilotage, elle doit trancher. */
.parc-tbl tfoot td {
  border-top: 2px solid var(--border-lit);
  font-family: var(--font-num); font-size: 12.5px; font-weight: 700; color: var(--fg);
  padding-top: 10px;
}
.parc-tbl tfoot td:first-child {
  font-family: var(--font); font-weight: 600; font-size: 10.5px;
  letter-spacing: .09em; text-transform: uppercase; color: var(--muted);
}

/* Chiffre cliquable dans le parc (impayé, tickets à faire) : il doit se lire
   comme une donnée, pas comme un lien — pas de cyan ni de soulignement au repos,
   la couleur du chiffre est déjà porteuse de sens. L'affordance vient du survol
   et du focus clavier. */
.chiffre-lien { display: inline-block; color: inherit; }
.chiffre-lien:hover { text-decoration: none; }
.chiffre-lien:hover .du,
.chiffre-lien:hover { color: var(--accent); }
.chiffre-lien .small { display: block; }
.chiffre-lien:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* --- Pagination --- */
.pager { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.pager-etat { font-size: 11.5px; color: var(--muted); }
/* Un bouton « désactivé » est ici un <span> : il ne doit ni être cliquable ni
   passer au clavier, mais garder l'encombrement du lien qu'il remplace. */
.pager [aria-disabled="true"] { opacity: .4; cursor: default; pointer-events: none; }

/* --- Sélecteur de statut d'une facture (modale) --- */
/* Trois états côte à côte, l'actuel en relief : on voit d'un coup OÙ l'on est et
   VERS QUOI on peut aller, dans les deux sens. Un enchaînement de boutons
   « Marquer envoyée » puis « Marquer payée » laissait croire à un parcours
   obligatoire et interdisait brouillon → payée. */
.inv-statut { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.inv-statut-label {
  font-size: 10.5px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted);
}
.segmented {
  display: inline-flex; align-items: stretch;
  border: 1px solid var(--border); border-radius: 8px; overflow: hidden;
  background: var(--bg-2);
}
/* Les formulaires ne doivent pas casser l'alignement du groupe. */
.segmented form { display: flex; margin: 0; }
.seg {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 6px 13px; border: 0; background: none; cursor: pointer;
  color: var(--muted); font: 500 12px var(--font);
  border-right: 1px solid var(--border);
}
.segmented > *:last-child .seg,
.segmented > .seg:last-child { border-right: 0; }
.seg:hover { color: var(--fg); background: rgba(255, 255, 255, .04); }
/* L'état courant n'est pas cliquable : c'est un repère, pas une action. */
.seg.on { cursor: default; color: var(--fg); background: rgba(255, 255, 255, .07); font-weight: 600; }
.seg.on.accent { color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); }
.seg.on.ok { color: var(--success); box-shadow: inset 0 -2px 0 var(--success); }
.seg.on:not(.accent):not(.ok) { box-shadow: inset 0 -2px 0 var(--muted); }

/* --- Mémo : grande modale (⌘M), éditeur mis en forme --- */
/* Il était un encart du tableau de bord jusqu'au 24/09/2026 : trop étroit pour
   écrire, absent des autres écrans. C'est désormais une <dialog> ouverte par
   showModal() — templates/_memo.html.twig. */

/* Contenu réservé aux lecteurs d'écran (utilitaire absent de cette feuille). */
.kl-sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* La boîte : large et haute — on y écrit, on n'y jette pas un œil. Même habit que
   les modales du hub (.mdl-box). `dvh` = hauteur visible réelle (barres mobiles). */
.memo-dlg {
  width: min(980px, calc(100vw - 48px));
  height: min(840px, calc(100vh - 64px));
  height: min(840px, calc(100dvh - 64px));
  max-width: none; max-height: none;
  margin: auto; padding: 0;
  color: var(--fg);
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border-lit); border-radius: 14px;
  box-shadow: 0 40px 110px rgba(0, 0, 0, .78), 0 0 0 1px rgba(53, 208, 242, .14);
  overflow: hidden;
}
/* `:not([open])` n'a pas besoin de règle : une <dialog> fermée est display:none. */
.memo-dlg[open] { display: flex; flex-direction: column; }
.memo-dlg::backdrop { background: rgba(4, 7, 13, .74); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
/* La page dessous ne défile plus pendant l'écriture. */
html.memo-ouverte, html.memo-ouverte body { overflow: hidden; }

.memo-tete { flex: none; display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); }
.memo-titre { margin: 0; font-family: var(--font-brand); font-size: 13px; text-transform: uppercase; letter-spacing: .13em; }
.memo-titre::before { content: ''; display: inline-block; width: 3px; height: 13px; border-radius: 2px; margin-right: 9px; vertical-align: -2px; background: linear-gradient(180deg, var(--accent), var(--accent-2)); box-shadow: 0 0 10px rgba(53, 208, 242, .55); }
.memo-fermer {
  width: 30px; height: 30px; flex: none; border-radius: 7px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .04); border: 1px solid var(--border-lit); color: var(--muted);
  font-size: 19px; line-height: 1;
}
.memo-fermer:hover { border-color: var(--accent); color: var(--accent); }

/* Largeur réservée une fois pour toutes : le libellé passe de « Modifié… » à
   « Enregistré 14:32 » sans pousser le titre. */
.memo-etat {
  margin-left: auto; flex: 0 0 auto;
  min-width: 15ch; text-align: right; white-space: nowrap;
  font-family: var(--font-num); font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dim); font-variant-numeric: tabular-nums;   /* l'horodatage ne frémit pas */
  transition: color .2s ease;
}
.memo-etat[data-etat="envoi"] { color: var(--accent); }
.memo-etat[data-etat="ok"] { color: var(--success); }
.memo-etat[data-etat="echec"],
.memo-etat[data-etat="session"] { color: var(--danger); }
.memo-etat[data-etat="hors"],
.memo-etat[data-etat="conflit"],
.memo-etat[data-etat="long"] { color: var(--warn); }

/* Barre d'outils : huit boutons, rien d'autre. Elle défile sous le doigt plutôt que
   de passer sur deux lignes sur un petit écran. */
.memo-outils {
  flex: none; display: flex; align-items: center; gap: 3px;
  padding: 7px 12px; border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none;
}
.memo-outils::-webkit-scrollbar { display: none; }
.memo-bt {
  width: 34px; height: 32px; flex: none; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 7px; border: 1px solid transparent; background: none;
  color: var(--muted); cursor: pointer; font: 600 14px/1 var(--font);
  transition: color .12s ease, background-color .12s ease;
}
.memo-bt:hover { color: var(--fg); background: rgba(255, 255, 255, .05); }
.memo-bt[aria-pressed="true"] { color: var(--accent); background: var(--accent-soft); border-color: rgba(53, 208, 242, .38); }
.memo-bt:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.memo-bt b { font-weight: 800; }
.memo-bt i { font-family: Georgia, 'Times New Roman', serif; font-size: 16px; font-weight: 400; }
.memo-bt s { font-weight: 500; }
.memo-bt-h { font-family: var(--font-brand); font-weight: 700; font-size: 15px; }
.memo-bt svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.memo-bt svg circle, .memo-bt svg text { fill: currentColor; stroke: none; }
.memo-bt svg text { font: 700 6.5px var(--font-num); }
.memo-sep { flex: none; width: 1px; height: 18px; margin: 0 6px; background: var(--border-lit); }

/* Ligne de saisie du lien. `:not([hidden])` OBLIGATOIRE : un display:flex nu
   l'emporterait sur l'attribut hidden. */
.memo-lien:not([hidden]) {
  flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 8px 12px; border-bottom: 1px solid var(--border); background: rgba(255, 255, 255, .02);
}
.memo-lien input { flex: 1 1 240px; min-width: 0; font-size: 16px; }   /* 16 px : pas de zoom iOS */
.memo-lien input.invalide { border-color: var(--danger); box-shadow: 0 0 0 1px rgba(251, 95, 122, .35); }

/* Messages et arbitrages. Même règle `:not([hidden])` que ci-dessus. */
.memo-alerte:not([hidden]) {
  flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: 10px 14px 0; padding: 8px 10px;
  border: 1px solid rgba(251, 191, 36, .45); border-radius: var(--radius-sm);
  background: rgba(251, 191, 36, .08); color: var(--warn); font-size: 12px;
}

/* La page d'écriture : elle seule défile. Colonne de lecture centrée, ~75 signes. */
.memo-zone { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; cursor: text; }
.memo-ed {
  position: relative; box-sizing: border-box;
  min-height: 100%; max-width: 760px; margin: 0 auto;
  padding: 22px clamp(16px, 5vw, 48px) 48px;
  outline: none;
  /* 16 px À TOUTES LES LARGEURS : en dessous, iOS Safari zoome à la mise au point. */
  font: 400 16px/1.6 var(--font); color: var(--fg);
  overflow-wrap: anywhere;
  caret-color: var(--accent);
}
.memo-ed.vide::before { content: attr(data-placeholder); position: absolute; color: var(--dim); pointer-events: none; }
.memo-ed > :first-child { margin-top: 0; }
.memo-ed h3 { margin: 20px 0 6px; font: 600 18px/1.35 var(--font-brand); letter-spacing: .01em; text-transform: none; color: #fff; }
.memo-ed p { margin: 0 0 8px; }
.memo-ed ul, .memo-ed ol { margin: 4px 0 8px; padding-left: 24px; }
.memo-ed li { margin: 2px 0; padding-left: 2px; }
.memo-ed li::marker { color: var(--accent); }
.memo-ed b, .memo-ed strong { font-weight: 700; color: #fff; }
.memo-ed s, .memo-ed strike, .memo-ed del { color: var(--muted); }
.memo-ed a { color: var(--accent); text-decoration: underline; text-decoration-color: rgba(53, 208, 242, .45); text-underline-offset: 2px; }
.memo-ed blockquote { margin: 8px 0; padding-left: 12px; border-left: 2px solid var(--border-lit); color: var(--muted); }

/* Liste à cocher (27/09/2026) : <ul class="kl-todo">, élément coché <li data-fait="1">.
   La case est DESSINÉE (::before, et la coche en ::after) : aucun <input>, que
   MemoHtml jetterait et qui casse la sélection d'un contenteditable. La colonne de
   gauche (padding-left) est la zone cliquable, repérée au clic par le script : on
   règle sa largeur ici, le script la relit. Un ::before en position absolue n'est
   pas barré avec le texte. `content: … / …` : ce que lit un lecteur d'écran — sur un
   contenu NON vide, sinon Chrome ignore le pseudo-élément, texte de remplacement
   compris ; le glyphe ☐ / ☑ est éteint par font-size: 0 (la boîte garde sa taille). */
.memo-ed ul.kl-todo { list-style: none; padding-left: 0; }
.memo-ed ul.kl-todo > li { position: relative; margin: 3px 0; padding-left: 30px; }
.memo-ed ul.kl-todo > li::before {
  content: '\2610'; content: '\2610' / 'À faire, ';
  font-size: 0;
  position: absolute; left: 5px; top: 4px;
  width: 17px; height: 17px; box-sizing: border-box;
  border: 1.5px solid var(--muted); border-radius: 5px;
  background: rgba(255, 255, 255, .03);
  cursor: pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
.memo-ed ul.kl-todo > li:hover::before { border-color: var(--accent); }
.memo-ed ul.kl-todo > li[data-fait="1"]::before { content: '\2611'; content: '\2611' / 'Fait, '; background: var(--accent); border-color: var(--accent); }
.memo-ed ul.kl-todo > li[data-fait="1"]::after {
  content: ''; position: absolute; left: 11px; top: 7px;
  width: 5px; height: 9px; box-sizing: border-box;
  border: solid #06121c; border-width: 0 2px 2px 0;
  transform: rotate(45deg); pointer-events: none;
}
.memo-ed ul.kl-todo > li[data-fait="1"] { color: var(--muted); text-decoration: line-through; text-decoration-color: rgba(127, 139, 166, .75); }
.memo-ed ul.kl-todo > li[data-fait="1"] :is(b, strong, a) { color: inherit; }

/* Téléphone : plein écran, et de la marge sous le texte pour que la dernière ligne
   ne reste pas cachée derrière le clavier. */
@media (max-width: 899px) {
  .memo-dlg { width: 100vw; height: 100vh; height: 100dvh; border: 0; border-radius: 0; }
  .memo-ed { padding: 16px 16px 40vh; }
}

/* Au doigt : cibles de 44 px. La barre tient alors sans défiler dès 372 px (huit
   boutons collés, séparateurs resserrés) ; une rangée de liste à cocher fait 44 px
   de haut et sa case, 44 px de large. */
@media (max-width: 767px), (pointer: coarse) {
  .memo-outils { gap: 0; padding: 0 4px; }
  .memo-bt { width: 44px; height: 44px; }
  .memo-sep { margin: 0 2px; }
  .memo-fermer { width: 44px; height: 44px; }
  .memo-lien .btn, .memo-lien input, .memo-alerte .btn { min-height: 44px; }
  .memo-ed ul.kl-todo > li { box-sizing: border-box; min-height: 44px; margin: 0; padding: 9px 0 9px 44px; }
  .memo-ed ul.kl-todo > li::before { left: 12px; top: 12px; width: 20px; height: 20px; border-radius: 6px; }
  .memo-ed ul.kl-todo > li[data-fait="1"]::after { left: 19px; top: 15px; width: 6px; height: 11px; }
}

/* ============ Météo : bandeau 15 jours + détail horaire ============ */
/*
  RÈGLE QUI DÉBLOQUE TOUT LE RESTE, et la seule mesurée deux fois de façon
  indépendante : sans elle, à 375 px la carte météo fait 1052 px de large,
  document.scrollWidth monte à ~1074 pour un écran de 375, la piste ne défile
  JAMAIS (clientWidth == scrollWidth) et c'est toute la page du dashboard qui part
  en travers — l'encart voisin compris. Cause : .dash-weather est un item de
  grille, donc min-width:auto = taille min-content, et la min-content de la piste
  vaut les quinze colonnes côte à côte ; la contrainte traverse le conteneur à
  défilement et élargit la piste de grille. Le défaut PRÉEXISTE à ce lot (l'ancien
  bandeau de chips produisait déjà ~1094 px), mais tout le dispositif mobile
  d'ici en dépend.
  Mettre min-width:0 sur .mto ou sur .mto-scroll ne change RIEN : la contrainte
  doit être coupée au niveau de l'item de grille. Généralisée le 24/09/2026 :
  `.dash > .card { min-width: 0 }` (grille des encarts), pour tous les encarts.
*/

.mto {
    /* Largeur PLANCHER d'une colonne : en dessous, la piste défile au lieu de
       compresser les étiquettes. --n (nombre de jours) est posé par le gabarit. */
    --mto-col: 42px;
    /* Hauteur de la zone de courbe. */
    --mto-h: 92px;
}

/* 375 px : quinze colonnes lisibles réclament ~630 px. On ne tronque pas les
   étiquettes, on ne les incline pas et on ne les empile pas (illisible au doigt) :
   la piste défile horizontalement — geste déjà en place sur l'ancien bandeau — et
   comme la prévision commence TOUJOURS par aujourd'hui, rien d'important n'est
   caché au premier coup d'œil.
   Le rembourrage vertical n'est pas décoratif : overflow-x:auto force overflow-y à
   « auto » quoi qu'on déclare, donc tout ce qui dépasse du bord haut d'une colonne
   est rogné. 5 px laissent passer l'anneau de focus et le halo d'aujourd'hui. */
.mto-scroll {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scroll-padding-inline: 4px;
    padding: 5px 4px 8px;
    margin: -5px -4px 0;
    -webkit-overflow-scrolling: touch;
}

/* La piste fait au moins toute la largeur disponible, et au moins la largeur
   plancher des quinze colonnes : c'est ce `max()` qui décide « ça rentre » ou
   « ça défile », une seule fois, pour toutes les rangées. */
.mto-track {
    position: relative;
    min-width: max(100%, calc(var(--n) * var(--mto-col)));
}

/* Une rangée = une ligne de colonnes égales. En flex plutôt qu'en grid : pas de
   var() dans un repeat(), donc aucune déclaration invalide possible. */
.mto-row {
    display: flex;
    align-items: flex-end;
    /* Les rangées ne reçoivent aucun pointeur : c'est la couche de liens, au-dessus,
       qui capte le survol et le clic sur TOUTE la hauteur de la colonne. */
    pointer-events: none;
}
.mto-cell { flex: 1 1 0; min-width: 0; text-align: center; }

/* --- Rangée 1 : jour + numéro --- */
/* Le rembourrage HAUT sépare l'étiquette du bord de la carte : collée dessus, la
   frise donnait l'impression de déborder. Il est posé ici et non sur .mto-scroll,
   dont le padding compense le rognage du dépassement (anneau de focus, halo
   d'aujourd'hui) et n'est donc pas un réglage d'espacement. */
.mto-heads { padding-top: 10px; padding-bottom: 3px; }
.mto-dow { display: block; font-size: 9.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
.mto-dnum { display: block; font-family: var(--font-num); font-size: 12.5px; font-weight: 700; line-height: 1.25; color: var(--dim); }
.mto-head.is-auj .mto-dow,
.mto-head.is-auj .mto-dnum { color: var(--accent); }

/* --- Rangée 2 : icône --- */
.mto-icos { margin-bottom: 2px; }
.mto-ico { font-size: 22px; line-height: 1.2; }

/* --- Rangées 3 et 5 : les chiffres --- */
.mto-tmax .mto-cell { font-family: var(--font-num); font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.3; }
.mto-tmin .mto-cell { font-family: var(--font-num); font-size: 11.5px; font-variant-numeric: tabular-nums; line-height: 1.4; padding-top: 2px; color: var(--muted); }
.mto-tmax .mto-cell.is-auj,
.mto-tmin .mto-cell.is-auj { color: var(--accent); }
.mto-nodata { flex: 1 1 100%; text-align: left; font-size: 12px; color: var(--muted); }

/* --- Rangée 4 : le graphique --- */
.mto-plot { position: relative; height: var(--mto-h); }
.mto-svg { display: block; width: 100%; height: 100%; }

/* Aire min→max : dégradé volontairement très faible. Sa couleur ne veut RIEN dire
   (ce n'est pas une échelle de chaleur) : elle donne l'amplitude. */
.mto-g1 { stop-color: var(--accent); stop-opacity: .22; }
.mto-g2 { stop-color: var(--accent-2); stop-opacity: .06; }

/* Bâtonnet vertical du jour : amplitude d'une seule journée, lisible même isolée. */
.mto-tige { fill: none; stroke: rgba(255, 255, 255, .07); stroke-width: 6; stroke-linecap: round; }
/* La teinte ne sert qu'à deux seuils qui se DÉCIDENT : le gel et la forte chaleur.
   Un dégradé arc-en-ciel sur quinze jours serait du bruit. */
.mto-t-gel { stroke: rgba(53, 208, 242, .40); }
.mto-t-chaud { stroke: rgba(251, 191, 36, .36); }
.mto-t-halo { stroke: rgba(53, 208, 242, .15); stroke-width: 16; }
.mto-t-auj { stroke: rgba(53, 208, 242, .55); }
.mto-aujguide { fill: none; stroke: var(--accent); stroke-width: 1; stroke-dasharray: 2 5; opacity: .28; }

.mto-courbe { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.mto-c-max { stroke: var(--fg); stroke-width: 1.7; }
.mto-c-min { stroke: var(--accent); stroke-width: 1.4; opacity: .75; }
.mto-pts { fill: none; stroke: var(--fg); stroke-width: 3.4; stroke-linecap: round; }

.mto-gelzone { fill: rgba(53, 208, 242, .055); }
.mto-zeroline { fill: none; stroke: var(--accent); stroke-width: 1; stroke-dasharray: 3 4; opacity: .45; }
/* Collée au bord DROIT : à gauche, son fond opaque découpait un carré dans le halo
   cyan de la colonne d'aujourd'hui, qui est toujours la première. */
.mto-zerolab {
    position: absolute; right: 0; transform: translateY(-50%);
    padding: 0 3px; border-radius: 3px;
    background: var(--card);
    font-family: var(--font-num); font-size: 9.5px; color: var(--accent); opacity: .85;
    pointer-events: none;
}

/* --- Rangée 6 : la pluie --- */
.mto-rains { margin-top: 7px; }
.mto-eau { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.mto-eau-piste {
    display: flex; align-items: flex-end; justify-content: center;
    width: 58%; max-width: 22px; height: 24px;
    border-radius: 3px 3px 0 0;
    /* Pas de cadre : quinze cases vides sur une quinzaine sèche font du bruit.
       Une simple ligne de sol suffit à donner l'appui des barres. */
    background: rgba(255, 255, 255, .018);
    box-shadow: 0 1px 0 var(--border);
}
.mto-eau-barre { display: block; width: 100%; min-height: 2px; border-radius: 3px; background: rgba(53, 208, 242, .28); }
.mto-eau-barre.moyen { background: rgba(53, 208, 242, .55); }
.mto-eau-barre.fort { background: var(--accent); box-shadow: 0 0 10px rgba(53, 208, 242, .40); }
.mto-eau-val { display: block; min-height: 13px; font-family: var(--font-num); font-size: 9.5px; font-weight: 600; color: var(--muted); }
.mto-eau-val.fort { color: var(--accent); }

/* --- Rangée 7 : le vent, seulement s'il y a du vent --- */
.mto-vents .mto-cell { min-height: 14px; font-family: var(--font-num); font-size: 9.5px; font-weight: 600; color: var(--warn); }

/* --- Couche de visée : un lien par jour, pleine hauteur --- */
.mto-hits { position: absolute; inset: 0; display: flex; }
.mto-hit {
    flex: 1 1 0; min-width: 0;
    position: relative; display: block;
    border-radius: var(--radius-sm);
    scroll-snap-align: center;
    /* Les liens sont VIDES : rien n'hérite du cyan de `a` (app.css l. 61). On
       neutralise quand même couleur et soulignement, patron déjà suivi par
       .site-card, .parc-nom, .pal-item, .top-new. */
    color: inherit; text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: background .12s ease, box-shadow .12s ease;
}
.mto-hit:hover, .mto-hit:focus { text-decoration: none; }
/* Samedi et dimanche en très léger retrait : organiser une semaine de boutique
   demande de savoir où tombe le week-end. */
.mto-hit.is-we { background: rgba(255, 255, 255, .028); }
.mto-hit.is-auj { background: linear-gradient(180deg, rgba(53, 208, 242, .13), rgba(53, 208, 242, .02) 62%, transparent); }
.mto-hit.is-auj::before {
    content: '';
    position: absolute; left: 22%; right: 22%; top: 0; height: 2px;
    border-radius: 0 0 2px 2px;
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent);
}
.mto-hit:hover { background: rgba(53, 208, 242, .09); }
.mto-hit.is-auj:hover { background: linear-gradient(180deg, rgba(53, 208, 242, .21), rgba(53, 208, 242, .07)); }
/* Focus : box-shadow INSET et pas outline. Un outline se peint à l'extérieur de
   la boîte et serait rogné par le conteneur à défilement ; un inset, jamais.
   Le survol LAVE la colonne, le focus l'ENCADRE : les deux états restent
   distinguables côte à côte. */
.mto-hit:focus-visible {
    outline: none;
    background: rgba(53, 208, 242, .12);
    box-shadow: inset 0 0 0 2px var(--accent), 0 0 16px rgba(53, 208, 242, .20);
}

/* --- En-tête de l'encart --- */
.mto-bar { margin-bottom: 10px; gap: 10px; flex-wrap: wrap; }
.mto-meta { display: flex; align-items: baseline; gap: 10px; margin-left: auto; }
.mto-ech { font-family: var(--font-num); font-size: 10.5px; color: var(--muted); white-space: nowrap; }
.mto-leg { display: none; }
@media (min-width: 680px) {
    .mto-leg { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; color: var(--muted); white-space: nowrap; }
}
/* Sous 680 px, « 15 jours » est redondant avec les quinze colonnes et le texte
   d'aide : on le retire plutôt que de faire passer la barre sur trois lignes. */
@media (max-width: 679px) { .mto-compte { display: none; } }
.mto-lg { display: inline-block; width: 12px; height: 0; border-top: 2px solid; border-radius: 2px; }
.mto-lg-max { color: var(--fg); }
.mto-lg-min { color: var(--accent); }
.mto-lg-eau { width: 5px; height: 9px; border: 0; border-radius: 2px; background: rgba(53, 208, 242, .55); }
.mto-help { margin-top: 10px; }

/* L'espace gagné sur grand écran sert à RESPIRER, pas à étirer : la zone de
   courbe grandit, les traits s'épaississent, les chiffres restent des chiffres. */
@media (min-width: 900px) {
    .mto { --mto-col: 46px; --mto-h: 118px; }
    .mto-ico { font-size: 25px; }
}
@media (min-width: 1400px) {
    .mto { --mto-h: 150px; }
    .mto-tige { stroke-width: 9; }
    .mto-t-halo { stroke-width: 22; }
    .mto-ico { font-size: 28px; }
    .mto-tmax .mto-cell { font-size: 14.5px; }
    .mto-tmin .mto-cell { font-size: 12.5px; }
    .mto-eau-piste { height: 30px; max-width: 26px; }
    .mto-eau-val, .mto-vents .mto-cell { font-size: 10.5px; }
}

/* ============ Météo : détail d'une journée (fragment de modale) ============ */
/*
  container-type sur le panneau : la densité des étiquettes doit dépendre de la
  largeur du PANNEAU, pas de celle de l'écran. Le fragment vit dans une modale
  (max-width 1120 px, plein écran sous 700 px) : une media query piloterait une
  grandeur qu'elle ne mesure pas.
*/
.mtj { container-type: inline-size; }

.mtj-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.mtj-ico { font-size: 26px; line-height: 1; flex: none; }
.mtj-titre { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
/* ::first-letter et NON text-transform:capitalize : en français seule la première
   lettre prend la majuscule (« Jeudi 12 septembre », pas « Jeudi 12 Septembre »).
   Les enfants d'un conteneur flex sont blockifiés, la pseudo-classe s'applique. */
.mtj-titre strong::first-letter { text-transform: uppercase; }
.mtj-titre strong { font-size: 14px; }
.mtj-sub { font-size: 11px; color: var(--muted); font-family: var(--font-num); }
.mtj-step {
    flex: none;
    /* 44 px : la règle de cible tactile du projet. */
    min-width: 44px; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--border-lit); border-radius: var(--radius-sm);
    background: rgba(255, 255, 255, .03); color: var(--fg);
    font-size: 18px; line-height: 1; text-decoration: none;
    -webkit-tap-highlight-color: transparent;
}
.mtj-step:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.mtj-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Bord de fenêtre : un <span>, pas un lien désactivé — aucun lien mort. */
.mtj-off { opacity: .3; }
/* Sous 520 px, deux cibles de 44 px et « jeudi 12 septembre » ne tiennent pas sur
   une ligne : le titre prend sa propre rangée plutôt que d'être tronqué. */
@container (max-width: 519px) {
    .mtj-head { flex-wrap: wrap; }
    .mtj-titre { order: -1; flex: 1 1 100%; }
}

/* Rangées d'étiquettes : MÊME découpe en colonnes que le SVG. L'alignement est
   structurel, il ne peut pas dériver. */
.mtj-row { display: flex; }
.mtj-cell {
    flex: 1 1 0; min-width: 0; text-align: center;
    font-family: var(--font-num); font-size: 9.5px; color: var(--dim);
    /* overflow visible : sur un panneau étroit le texte fait 11 px pour une cellule
       de 14 — les voisines étant invisibles, le débord ne gêne personne. */
    overflow: visible; white-space: nowrap;
}
/* visibility et JAMAIS display:none : display retirerait la cellule de la rangée
   et auto-placerait les survivantes dans les premières colonnes — l'axe mentirait
   de plus de 100 px, faisant lire « il pleut à 21 h » sur les barres de 9 h. */
.mtj-row .j1 { visibility: hidden; }
@container (min-width: 620px) { .mtj-row .j1 { visibility: visible; } }
.mtj-temps .mtj-cell { font-size: 11px; font-weight: 700; color: var(--fg); padding-bottom: 2px; }
.mtj-axe { margin-top: 5px; }
.mtj-axe .j3 { color: var(--muted); font-weight: 600; }

/* Le tracé. Hauteur FIXE et égale à la hauteur du viewBox : 1 unité = 1 px en
   vertical, donc les étiquettes posées en pixels retombent pile sur les traits. */
.mtj-plot { position: relative; height: 174px; }
.mtj-svg { display: block; width: 100%; height: 174px; }

.mtj-g0 { stop-color: var(--warn); stop-opacity: .30; }
.mtj-g1 { stop-color: var(--warn); stop-opacity: 0; }
.mtj-aire { stroke: none; }
.mtj-ligne { fill: none; stroke: var(--warn); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.mtj-nuit { fill: rgba(124, 108, 246, .07); }
.mtj-gelzone { fill: rgba(53, 208, 242, .06); }
.mtj-zero { fill: none; stroke: var(--accent); stroke-width: 1; stroke-dasharray: 2 3; opacity: .55; }
.mtj-nuagebox { fill: none; stroke: var(--border); stroke-width: 1; }
.mtj-nuage { fill: var(--fg); }
.mtj-barre { fill: var(--accent); }
.mtj-coiffe { fill: #eaf9ff; }
/* Probabilité inconnue (queue de fenêtre) : ni barre, ni « 0 % ». */
.mtj-inc { fill: rgba(255, 255, 255, .05); }
.mtj-sol { fill: none; stroke: var(--border-lit); stroke-width: 1; }
.mtj-moitie { fill: none; stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 4; }
.mtj-voile { fill: var(--bg); opacity: .5; }
.mtj-now { fill: none; stroke: var(--accent); stroke-width: 1.5; }

.mtj-lab {
    position: absolute; transform: translateY(-50%); pointer-events: none;
    font-family: var(--font-num); font-size: 9px; color: var(--dim);
    background: none;
    /* Pas de fond opaque : un liseré suffit, et rien ne découpe le dessin. */
    text-shadow: 0 0 4px var(--card), 0 0 4px var(--card);
}
.mtj-lab-zero { right: 0; color: var(--accent); }
.mtj-lab-50 { left: 0; }

.mtj-leg { display: flex; flex-direction: column; gap: 4px; margin: 10px 0 0; font-size: 10.5px; color: var(--muted); }
.mtj-leg span { display: inline-flex; align-items: center; gap: 6px; }
.mtj-leg i { width: 12px; height: 3px; border-radius: 2px; flex: none; }
.mtj-leg .sw-t { background: var(--warn); }
.mtj-leg .sw-n { height: 9px; background: linear-gradient(90deg, rgba(233, 238, 248, .08), var(--fg)); }
.mtj-leg .sw-p { height: 9px; background: var(--accent); }
.mtj-leg .sw-m { height: 9px; background: linear-gradient(180deg, #eaf9ff 0 34%, var(--accent) 34% 100%); }
.mtj-leg .sw-v { height: 9px; background: rgba(124, 108, 246, .35); }
@container (min-width: 620px) { .mtj-leg { flex-direction: row; flex-wrap: wrap; gap: 6px 14px; } }

.mtj-tab { margin-top: 12px; }
.mtj-tab summary { cursor: pointer; color: var(--muted); font-size: 11.5px; }
.mtj-tab summary:hover { color: var(--accent); }
.mtj-tab .table-wrap { margin-top: 8px; }
.mtj-tab td, .mtj-tab th { font-size: 11.5px; }
.mtj-passe { opacity: .5; }

/* --- Modale (rendez-vous) --- */
/* display:flex l'emporterait sur la règle par défaut de [hidden] : on la rétablit. */
.modal[hidden] { display: none; }
.modal {
    position: fixed; inset: 0; z-index: 100;
    display: flex; align-items: center; justify-content: center;
    padding: 16px;
    background: rgba(8, 10, 16, .68);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}
.modal-box {
    width: 100%; max-width: 520px; max-height: 90vh; overflow-y: auto;
    background: var(--card); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 20px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .5);
}

/* ============ Vitesse serveur ============ */
/* Sparkline SVG : largeur fluide, hauteur fixe. preserveAspectRatio="none"
   étire le tracé, d'où vector-effect côté SVG pour garder un trait constant. */
.spark { display: block; width: 100%; height: 30px; min-width: 90px; overflow: visible; }
.spark-cell { min-width: 120px; }
.spark-vide { color: var(--muted); font-size: 12px; }
.spark-bornes { display: block; font-size: 10.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.num { font-variant-numeric: tabular-nums; }
.warn-txt { color: var(--warn); }

.perf-row { padding: 12px 0; border-bottom: 1px solid var(--border); }
.perf-row:last-of-type { border-bottom: 0; }
.perf-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; }

/* Barre empilée : la part réseau/hébergeur puis la part applicative. */
.perf-bar { display: flex; height: 9px; margin: 8px 0 5px; border-radius: 2px; overflow: hidden; background: rgba(255, 255, 255, .05); border: 1px solid var(--border); }
.perf-bar > span { display: block; height: 100%; }
.perf-bar > span.reseau { background: var(--muted); }
.perf-bar > span.appli { background: linear-gradient(90deg, var(--accent), var(--accent-2)); box-shadow: 0 0 12px rgba(53, 208, 242, .45); }
.perf-legend { display: flex; flex-wrap: wrap; gap: 12px; font-size: 11px; color: var(--muted); }
.perf-legend i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 4px; }
.perf-legend i.reseau { background: var(--muted); }
.perf-legend i.appli { background: var(--accent); }
.perf-meta { margin-top: 6px; }
.perf-spark { margin-top: 8px; max-width: 420px; }
.perf-urls { margin-top: 12px; }
.perf-urls summary { cursor: pointer; color: var(--muted); }
.perf-urls summary:hover { color: var(--accent); }

/* ============ Dashboard pleine largeur ============ */
/* Seul le dashboard passe en pleine largeur : c'est un poste de pilotage.
   Les écrans de lecture gardent leur colonne à 1180 px. */
.container-full { max-width: none; padding-left: 22px; padding-right: 22px; }

/* Au-delà de 1400 px : voir public/css/dashboard.css (Alertes 1/3, Dernières actions 2/3). */

/* ============ Cockpit : finitions ============ */

/* Coins d'écoutille sur les panneaux qu'on veut mettre en avant.
   Réservé à .card.pk-h et aux cartes du dashboard : généralisé, ce serait du bruit. */
.dash > .card::after,
.card.hud::after {
  content: '';
  position: absolute; right: 8px; bottom: 8px;
  width: 10px; height: 10px;
  border-right: 1px solid var(--border-lit);
  border-bottom: 1px solid var(--border-lit);
  border-radius: 0 0 3px 0;
  pointer-events: none;
}

/* Cartes de tickets : plaque d'identification, liseré de priorité lumineux. */
.tk-card {
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border-color: var(--border);
  border-radius: var(--radius-sm);
}
.tk-card::before { box-shadow: 0 0 10px var(--accent); }
.tk-card.p3::before { box-shadow: 0 0 10px var(--danger); }
.tk-card.p2::before { box-shadow: 0 0 10px var(--warn); }
.tk-card:hover { border-color: var(--accent); box-shadow: 0 10px 30px rgba(53, 208, 242, .16); }
.tk-site { text-transform: uppercase; letter-spacing: .08em; font-size: 10px; color: var(--muted); }
.tk-age { font-size: 10.5px; }
.tk-act { border-radius: 4px; border-color: var(--border-lit); }
.tk-act:hover { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 12px rgba(53, 208, 242, .25); }

/* Listes compactes : le liseré gauche devient un voyant. */
.mini-list li { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; box-shadow: -1px 0 8px -2px var(--accent); }
.mini-list li.p3 { box-shadow: -1px 0 8px -2px var(--danger); }
.mini-list li.p2 { box-shadow: -1px 0 8px -2px var(--warn); }
.mini-list a:hover { background: rgba(53, 208, 242, .07); }
.ml-site { text-transform: uppercase; letter-spacing: .06em; font-size: 10px; }

/* Agenda : cases d'instrument, jour courant sous tension. */
.wk-day { background: rgba(255, 255, 255, .025); border-radius: var(--radius-sm); }
.wk-day.today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent), 0 0 22px rgba(53, 208, 242, .18); }
.wk-num { font-family: var(--font-num); }
.wk-dow { text-transform: uppercase; letter-spacing: .08em; font-size: 10px; }
.wk-ev { border-radius: 3px; background: var(--accent-soft); border-left-color: var(--accent); }
.wk-ev.allday { background: rgba(124, 108, 246, .14); border-left-color: var(--accent-2); }
.wk-ev b { font-family: var(--font-num); }


/* Pastilles de raccourci (clients). */
.pill { border-radius: 4px; text-transform: uppercase; letter-spacing: .07em; font-size: 10px; font-weight: 600; border-color: var(--border-lit); }
.pill:hover { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 12px rgba(53, 208, 242, .2); }
.pill.bo { border-color: rgba(124, 108, 246, .5); color: var(--accent-2); }

/* Jeton, zone vide, ascenseur. */
.token-box { border-color: var(--border-lit); background: rgba(255, 255, 255, .03); }
.empty { color: var(--muted); }
.empty .big { filter: drop-shadow(0 0 18px rgba(53, 208, 242, .35)); }
::-webkit-scrollbar-thumb { background: var(--border-lit); }
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

/* Sous-titre de page et barre d'outils. */
.page-sub { color: var(--muted); font-size: 12.5px; }
.section-pill { border-radius: 4px; text-transform: uppercase; letter-spacing: .07em; font-size: 10px; font-weight: 700; border-color: var(--border-lit); }
.nav-badge { box-shadow: 0 0 10px rgba(251, 95, 122, .7); border-radius: 4px; }

/* Modale : hublot au-dessus d'un pont assombri. */
.modal { background: rgba(4, 7, 13, .78); }
.modal-box { background: linear-gradient(180deg, var(--card-2), var(--card)); border-color: var(--border-lit); box-shadow: 0 30px 80px rgba(0, 0, 0, .7), 0 0 0 1px rgba(53, 208, 242, .14); }

/* Écran d'authentification : la console au démarrage. */
.auth-box .brand { justify-content: center; font-size: 14px; }
.auth-box .card { box-shadow: var(--lift), 0 0 60px rgba(53, 208, 242, .08); }

/* ============================================================
   COQUE HUD — projection sur verrière
   Cadre projeté, bandeau de télémétrie et rails d'icônes.
   Tout est PLAT et en `position: fixed` : voir la note ci-dessous
   sur la perspective, qui cassait cet ancrage.
   ============================================================ */

/* AUCUNE perspective ici.
   Un `perspective` (comme un `transform` ou un `filter`) sur un ancêtre crée un
   BLOC CONTENEUR pour ses descendants en `position: fixed` : le menu, la verrière
   et le bandeau cessaient d'être ancrés à la fenêtre et se mettaient à défiler
   avec la page. Sur une page longue, le rail n'était plus au centre de l'écran.
   C'est la raison pour laquelle la 3D d'ambiance a été retirée. */

/* ---------- Verrière : cadre projeté, jamais cliquable ---------- */
.visor { position: fixed; inset: 0; z-index: 40; pointer-events: none; }
/* Vignette + courbure : le bord de l'écran s'assombrit comme une visière. */
.visor::before {
  content: ''; position: absolute; inset: 0;
  background:
    radial-gradient(130% 105% at 50% 45%, transparent 55%, rgba(4, 7, 13, .5) 82%, rgba(3, 5, 10, .88) 100%);
}
/* Reflet d'objectif, très discret, en haut à gauche. */
.visor::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(128deg, rgba(53, 208, 242, .05) 0%, transparent 22%, transparent 78%, rgba(124, 108, 246, .04) 100%);
}

.visor-corner { position: absolute; width: 26px; height: 26px; border: 1px solid rgba(53, 208, 242, .5); }
.visor-corner.tl { top: 10px; left: 10px; border-right: 0; border-bottom: 0; border-radius: 5px 0 0 0; }
.visor-corner.tr { top: 10px; right: 10px; border-left: 0; border-bottom: 0; border-radius: 0 5px 0 0; }
.visor-corner.bl { bottom: 10px; left: 10px; border-right: 0; border-top: 0; border-radius: 0 0 0 5px; }
.visor-corner.br { bottom: 10px; right: 10px; border-left: 0; border-top: 0; border-radius: 0 0 5px 0; }

/* Graduations : l'échelle d'un instrument, en haut et en bas. */
.visor-ticks { position: absolute; left: 50%; transform: translateX(-50%); width: min(420px, 46vw); height: 7px;
  background: repeating-linear-gradient(90deg, rgba(53, 208, 242, .45) 0 1px, transparent 1px 14px); opacity: .55; }
.visor-ticks.top { top: 12px; }
.visor-ticks.bottom { bottom: 12px; }

/* Balayage lent : le seul mouvement permanent, presque invisible. */
.visor-scan { position: absolute; left: 0; right: 0; height: 140px;
  background: linear-gradient(180deg, transparent, rgba(53, 208, 242, .045), transparent);
  animation: balayage 9s linear infinite; }
@keyframes balayage { from { top: -140px; } to { top: 100%; } }

/* ---------- Bandeau supérieur ---------- */
.hud-top {
  position: fixed; top: 0; left: 0; right: 0; z-index: 45;
  display: flex; align-items: center; gap: 16px;
  height: 46px; padding: 0 44px;
  background: linear-gradient(180deg, rgba(8, 12, 20, .95), rgba(8, 12, 20, .72) 70%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.hud-top::after {
  content: ''; position: absolute; left: 44px; right: 44px; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(53, 208, 242, .5), rgba(124, 108, 246, .4), transparent);
}
.brand {
  font-family: var(--font-brand); font-size: 11.5px; letter-spacing: .3em; text-transform: uppercase;
  white-space: nowrap; color: var(--fg); font-weight: 500;
  display: inline-flex; align-items: center; gap: 9px;
}
.brand:hover { text-decoration: none; }
.brand::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px var(--accent); animation: pouls 2.6s ease-in-out infinite; }
@keyframes pouls { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
.brand b { font-weight: 700; background: linear-gradient(90deg, var(--accent), var(--accent-2)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.agency-name { margin-left: auto; color: var(--muted); font-size: 11.5px; letter-spacing: .04em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 26vw; }
a.agency-name:hover { color: var(--accent); text-decoration: none; }

/* Relevés permanents. */
.tele { display: flex; gap: 6px; align-items: center; min-width: 0; overflow-x: auto; scrollbar-width: none; }
/* Mesuré à 1280 px : trois chronos réclamaient 1472 px et poussaient « Ticket » puis
   « Aller à… » HORS de l'écran. La bande est donc rétrécissable partout, et c'est
   ELLE qui glisse — jamais le reste du bandeau qui s'en va. */
.tele::-webkit-scrollbar { display: none; }
.tele > * { flex: none; }
.tele-item {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 4px;
  border: 1px solid var(--border); background: rgba(255, 255, 255, .03);
  font-size: 11px; color: var(--muted);
}
.tele-item b { font-family: var(--font-num); font-size: 12px; color: var(--fg); font-weight: 700; }
/* Boîte de réception (08/10/2026) : lien vers la Messagerie ; allumé quand des mails attendent. */
a.tele-item.boite { text-decoration: none; white-space: nowrap; }
a.tele-item.boite:hover { border-color: var(--accent); color: var(--accent); }
a.tele-item.boite:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tele-item.boite b { color: var(--muted); }
.tele-item.boite.a-traiter { border-color: rgba(251, 191, 36, .55); color: var(--warn, #fbbf24); background: rgba(251, 191, 36, .08); }
.tele-item.boite.a-traiter b { color: var(--warn, #fbbf24); }
/* Solde bancaire (05/10/2026) : un relevé qui est un BOUTON (il ouvre la modale Banque). */
button.tele-item.bq { font: inherit; font-size: 11px; cursor: pointer; white-space: nowrap; }
.tele-item.bq:hover, .tele-item.bq[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.tele-item.bq:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Rentrée d'argent (tiroir-caisse, 05/10/2026) : le solde s'allume en vert quelques instants. */
.tele-item.bq.rentree { border-color: rgba(52, 211, 153, .6); color: var(--success); animation: bq-rentree 1.1s ease-out 3; }
.tele-item.bq.rentree b { color: var(--success); }
@keyframes bq-rentree {
  0% { box-shadow: 0 0 0 0 rgba(52, 211, 153, .55); }
  100% { box-shadow: 0 0 0 10px rgba(52, 211, 153, 0); }
}
@media (prefers-reduced-motion: reduce) { .tele-item.bq.rentree { animation: none; } }
.tele-item i { font-style: normal; font-size: 10px; opacity: .65; }
.tele-item.ok { border-color: rgba(52, 211, 153, .35); color: var(--success); }
.tele-item.ok b { color: var(--success); }
.tele-item.warn { border-color: rgba(251, 191, 36, .4); color: var(--warn); }
.tele-item.warn b { color: var(--warn); }
.tele-item.bad { border-color: rgba(251, 95, 122, .45); color: var(--danger); box-shadow: 0 0 14px rgba(251, 95, 122, .18); }
.tele-item.bad b { color: var(--danger); }
/* La semaine (07/10/2026) : valeur HT des heures facturables et jauge vers l'objectif hebdomadaire
   (Réglages → Mes préférences). Jauge de 3 px au pied du relevé ; verte une fois l'objectif atteint. */
.tele-semaine { position: relative; overflow: hidden; }
.tele-ht { font-family: var(--font-num); font-size: 11.5px; color: var(--fg); opacity: .85; white-space: nowrap; }
.tele-jauge { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: rgba(255, 255, 255, .07); pointer-events: none; }
.tele-jauge > span { display: block; height: 100%; width: var(--sem, 0%); background: var(--accent); box-shadow: 0 0 6px var(--accent); transition: width .4s ease; }
.tele-semaine.atteint { border-color: rgba(52, 211, 153, .45); }
.tele-semaine.atteint .tele-jauge > span { background: var(--success); box-shadow: 0 0 8px var(--success); }
@media (prefers-reduced-motion: reduce) { .tele-jauge > span { transition: none; } }
/* Téléphone : la place manque (un chrono en cours la prend) ; la jauge reste, le montant est dans l'infobulle. */
@media (max-width: 899px) { .tele-ht { display: none; } }
.ic { width: 15px; height: 15px; flex: none; }

/* ---------- Le chrono en cours, dans l'en-tête fixe ----------
   Posé À CÔTÉ du compteur de la semaine (demande du 22/09/2026). Il dit trois
   choses : sur quoi on travaille, depuis combien de temps, et comment arrêter.
   Le tout est un <form> : le bouton ■ est un vrai envoi, sans une ligne de script.
   Mobile d'abord — Franck le lit debout en boutique : le nom du ticket est
   tronqué (jamais replié : une barre d'en-tête qui grandit pousse tout l'écran),
   et la cible du bouton ne descend jamais sous 28 px. */
/* La zone du chrono est TOUJOURS dans le bandeau, même quand rien ne tourne :
   c'est elle que le script repeint quand on lance un chrono depuis une carte,
   sans recharger la page. `display: contents` la rend transparente à la mise en
   page — le formulaire reste exactement l'élément qu'il était dans la barre —
   et `:empty` la fait disparaître quand elle ne contient rien, y compris pour
   les rares moteurs sans `display: contents`. */
.chrono-zone { display: contents; }
.chrono-zone:empty { display: none; }
/* PLUSIEURS CHRONOS À LA SUITE (23/09/2026). Chaque nom rétrécit à mesure qu'il y
   en a : ce qui doit rester visible sans faire défiler le bandeau, c'est le
   COMPTEUR et le bouton ■. Un titre tronqué se relit en survol (title) ; un bouton
   d'arrêt hors de l'écran, c'est un chrono qu'on laisse tourner. */
.chrono-zone:has(.chrono-top:nth-of-type(2)) .chrono-nom { max-width: min(15vw, 150px); }
.chrono-zone:has(.chrono-top:nth-of-type(3)) .chrono-nom { max-width: min(11vw, 100px); }
.chrono-zone:has(.chrono-top:nth-of-type(4)) .chrono-nom { max-width: min(8vw, 76px); }

.chrono-top {
  display: inline-flex; align-items: center; gap: 6px;
  min-width: 0;                       /* sans lui, le nom long déborde du bandeau */
  padding: 2px 4px 2px 8px; border-radius: 4px;
  border: 1px solid rgba(53, 208, 242, .45);
  background: linear-gradient(90deg, rgba(53, 208, 242, .16), rgba(124, 108, 246, .1));
  box-shadow: 0 0 14px rgba(53, 208, 242, .16);
}
.chrono-pastille {
  width: 6px; height: 6px; flex: none; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 8px var(--accent);
  animation: pouls 2.2s ease-in-out infinite;
}
.chrono-nom {
  font-size: 11px; color: var(--fg); font-weight: 600;
  max-width: 24vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Nom d'un chrono rattaché à un ticket : un lien qui ouvre le ticket (01/10/2026). */
a.chrono-ticket { text-decoration: none; cursor: pointer; border-radius: 3px; }
a.chrono-ticket:hover, a.chrono-ticket:focus-visible { color: var(--accent); text-decoration: none; }
.chrono-compteur {
  font-family: var(--font-num); font-size: 12px; font-weight: 700;
  color: var(--accent); font-variant-numeric: tabular-nums;
  /* Chasse fixe RÉSERVÉE : sans elle, « 0:09:59 » → « 0:10:00 » fait sauter le
     bouton d'arrêt d'un pixel toutes les secondes, sous le doigt. */
  min-width: 7ch; text-align: right;
}
/* ---------- Relancer un chrono depuis une saisie ----------
   Cible tactile de 34 px au repos, 44 px au doigt (pointer: coarse) : la carte
   reste compacte à la souris sans devenir impossible à viser au téléphone. */
.tp-relance { display: inline-flex; margin-left: 8px; }
.tp-play {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border-lit); background: var(--bg-2); color: var(--muted);
  font-size: 12px; line-height: 1;
}
.tp-play:hover { color: var(--accent); border-color: var(--accent); }
.tp-facturee .tp-play { opacity: .75; }
@media (pointer: coarse) { .tp-play { width: 44px; height: 44px; font-size: 14px; } }

/* ---------- Chrono sans ticket, depuis l'en-tête fixe ----------
   <details> natif : pas de script, ouverture au clavier et au doigt. La boîte
   est posée SOUS l'en-tête, jamais dans le flux, pour ne pas pousser la barre. */
/* `static` et non `relative` : la boîte ci-dessous prend alors le HEADER FIXE pour
   bloc conteneur, hors de `.tele` — une bande qui défile (overflow-x) rogne tout ce
   qui dépasse sa hauteur, et la boîte ne s'affichait JAMAIS (mesuré : le centre de
   la boîte ouverte renvoyait la page dessous). */
.chrono-neuf { position: static; }
.chrono-neuf > .chrono-neuf-btn {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  min-height: 34px; padding: 4px 11px; border-radius: 999px;
  border: 1px solid var(--border-lit); background: var(--bg-2);
  color: var(--muted); font-family: inherit; font-size: 12px; font-weight: 600; white-space: nowrap;
}
/* DÈS QU'UN CHRONO TOURNE, l'étiquette laisse la place à un « + ». Le bouton ne
   disparaît jamais — on cumule les chronos, il faut pouvoir en ajouter un sans
   arrêter les autres — mais le bandeau est alors plein, et « + » dit la même
   chose en 12 px. */
.chrono-neuf > .chrono-neuf-btn .chrono-plus { display: none; font-size: 15px; line-height: 1; }
body:has(.chrono-top) .chrono-neuf > .chrono-neuf-btn .chrono-etiquette,
body:has(.chrono-top) .chrono-neuf > .chrono-neuf-btn .chrono-pastille { display: none; }
body:has(.chrono-top) .chrono-neuf > .chrono-neuf-btn .chrono-plus { display: inline; }
body:has(.chrono-top) .chrono-neuf > .chrono-neuf-btn { min-width: 34px; padding: 4px 10px; justify-content: center; }
.chrono-neuf > .chrono-neuf-btn:hover { color: var(--fg); border-color: var(--accent); }
.chrono-neuf > .chrono-neuf-btn[aria-expanded="true"] { color: var(--fg); border-color: var(--accent); }
.chrono-pastille.vide { background: transparent; border: 2px solid currentColor; }
/* « Chrono » : fenêtre CENTRÉE (<dialog> showModal, 28/09/2026), projet en auto-complétion.
   overflow visible : la liste du projet déborde de la boîte au lieu d'y être rognée. */
.chrono-dlg {
  width: min(440px, calc(100vw - 32px)); max-width: none;
  margin: auto; padding: 18px 18px 16px; overflow: visible;
  color: var(--fg); background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border-lit); border-radius: 14px;
  box-shadow: 0 40px 110px rgba(0, 0, 0, .78), 0 0 0 1px rgba(53, 208, 242, .14);
}
.chrono-dlg::backdrop { background: rgba(4, 7, 13, .74); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.chrono-dlg-form { display: flex; flex-direction: column; gap: 12px; }
.chrono-dlg-titre { margin: 0; font-family: var(--font-brand); font-size: 13px; text-transform: uppercase; letter-spacing: .13em; }
.chrono-dlg-titre::before { content: ''; display: inline-block; width: 3px; height: 13px; border-radius: 2px; margin-right: 9px; vertical-align: -2px; background: linear-gradient(180deg, var(--accent), var(--accent-2)); box-shadow: 0 0 10px rgba(53, 208, 242, .55); }
.chrono-dlg .field { margin: 0; }
.chrono-dlg input[type=text] { width: 100%; min-height: 42px; font-size: 16px; }
.chrono-dlg .kl-combo-liste { z-index: 5; max-height: min(320px, 45vh); }
.chrono-dlg-erreur { margin: 0; color: var(--danger); font-size: 13px; }
.chrono-dlg-erreur:empty { display: none; }
.chrono-dlg-actions { display: flex; justify-content: flex-end; gap: 8px; }
.chrono-dlg-actions .btn { min-height: 44px; justify-content: center; }
/* ✎ du chrono en cours (28/09/2026) : même habit que le « # ». */
.chrono-modifier .ic { width: 12px; height: 12px; }
.chrono-modifier { border-style: solid; }
@media (max-width: 560px) {
  .chrono-neuf > .chrono-neuf-btn .chrono-etiquette { display: none; }
  .chrono-neuf > .chrono-neuf-btn { padding: 4px 9px; min-width: 34px; justify-content: center; }
}

.chrono-stop {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none; border-radius: 4px;
  border: 1px solid var(--border-lit); background: rgba(255, 255, 255, .05);
  color: var(--danger); font-size: 9px; line-height: 1; cursor: pointer;
}
.chrono-stop:hover { background: rgba(251, 95, 122, .16); border-color: rgba(251, 95, 122, .55); }
/* Chrono oublié (passé le plafond) : la saisie sera plafonnée ET non facturable.
   Ça doit se voir AVANT l'arrêt, pas dans le message d'après. */
.chrono-top.bad {
  border-color: rgba(251, 95, 122, .6);
  background: linear-gradient(90deg, rgba(251, 95, 122, .2), rgba(251, 95, 122, .08));
  box-shadow: 0 0 14px rgba(251, 95, 122, .22);
}
.chrono-top.bad .chrono-compteur { color: var(--danger); }
.chrono-top.bad .chrono-pastille { background: var(--danger); box-shadow: 0 0 8px var(--danger); }
/* TROIS CHRONOS OU PLUS : les relevés d'alertes et de tickets s'effacent, comme ils
   le font déjà sur mobile dès le premier chrono. Ils se relisent sur le tableau de
   bord et dans les rails ; un bouton d'arrêt qu'il faut aller chercher, lui, est un
   chrono qu'on laisse tourner — donc une facture fausse. */
.tele:has(.chrono-top:nth-of-type(3)) .tele-item.tele-secondaire { display: none; }

/* ---------- Rails latéraux : les panneaux inclinés ---------- */
.hud-rail, .hud-side {
  position: fixed; top: 50%; z-index: 44;
}
.hud-rail { left: 8px; }
.hud-side { right: 8px; }
.hud-rail .rail-inner, .hud-side .rail-inner {
  display: flex; flex-direction: column; gap: 3px;
  padding: 9px 7px; border-radius: 14px;
  background: linear-gradient(180deg, rgba(19, 27, 43, .82), rgba(14, 20, 32, .74));
  border: 1px solid var(--border);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .05);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.hud-rail, .hud-side { transform: translateY(-50%); }

.rail-btn {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 34px; border-radius: 8px;
  /* Plus clair que --muted : seize icônes sans texte doivent rester lisibles
     du premier coup d'œil, pas seulement au survol. */
  color: #9aa7c2;
  transition: color .14s ease, background .14s ease, box-shadow .14s ease;
}
.rail-btn:hover { color: var(--accent); background: rgba(53, 208, 242, .1); text-decoration: none; }
.rail-btn .ic { width: 19px; height: 19px; }
.rail-btn.active { color: var(--accent); background: linear-gradient(180deg, rgba(53, 208, 242, .2), rgba(53, 208, 242, .08)); box-shadow: inset 0 0 0 1px rgba(53, 208, 242, .45), 0 0 18px rgba(53, 208, 242, .22); }
.rail-btn.active .ic { filter: drop-shadow(0 0 6px rgba(53, 208, 242, .8)); }
/* Ergot lumineux vers l'intérieur de l'écran : on voit d'un coup où l'on est. */
.hud-rail .rail-btn.active::before,
.hud-side .rail-btn.active::before {
  content: ''; position: absolute; top: 50%; width: 3px; height: 16px; margin-top: -8px;
  background: var(--accent); box-shadow: 0 0 10px var(--accent); border-radius: 2px;
}
.hud-rail .rail-btn.active::before { right: -8px; }
.hud-side .rail-btn.active::before { left: -8px; }
.rail-btn.quit:hover { color: var(--danger); background: rgba(251, 95, 122, .1); }

/* Libellé : apparaît au survol, à plat, hors du plan incliné pour rester net. */
.rail-label {
  position: absolute; top: 50%; transform: translateY(-50%);
  padding: 5px 10px; border-radius: 6px; white-space: nowrap;
  background: rgba(10, 15, 24, .96); border: 1px solid var(--border-lit);
  color: var(--fg); font-size: 11px; text-transform: uppercase; letter-spacing: .09em; font-weight: 600;
  opacity: 0; pointer-events: none; transition: opacity .14s ease;
  box-shadow: 0 10px 26px rgba(0, 0, 0, .55);
}
.hud-rail .rail-label { left: calc(100% + 14px); }
.hud-side .rail-label { right: calc(100% + 14px); }
.rail-btn:hover .rail-label, .rail-btn:focus-visible .rail-label { opacity: 1; }

.rail-count {
  position: absolute; top: 1px; right: 1px;
  min-width: 15px; height: 15px; padding: 0 3px; border-radius: 4px;
  background: var(--danger); color: #fff;
  font-family: var(--font-num); font-size: 9.5px; font-weight: 700; font-style: normal;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 10px rgba(251, 95, 122, .65);
}

/* ---------- Scène : le contenu, à plat, entre les rails ---------- */
body.hud-on main.container { max-width: 1180px; margin: 0 auto; padding: 62px 20px 44px; }
body.hud-on main.container-full { max-width: none; }
@media (min-width: 900px) {
  body.hud-on main.container { padding-left: 74px; padding-right: 74px; }
}
@media (min-width: 1500px) {
  body.hud-on main.container { padding-left: 20px; padding-right: 20px; }
}

/* ---------- Mobile : les rails descendent sous le pouce ---------- */
@media (max-width: 899px) {
  .hud-top { height: 42px; padding: 0 12px; gap: 10px; }
  .hud-top::after { left: 12px; right: 12px; }
  .agency-name { display: none; }
  /* `min-width:0` + défilement : c'est ce qui empêche le bandeau de CASSER quand le
     nom du ticket en cours s'ajoute aux quatre relevés. À 375 px, si tout ne tient
     pas, la bande de relevés glisse sous le doigt — la page, elle, ne bouge pas. */
  .tele { margin-left: auto; gap: 4px; min-width: 0; overflow-x: auto; scrollbar-width: none; }
  .tele::-webkit-scrollbar { display: none; }
  .tele > * { flex: none; }
  .tele-item { padding: 2px 6px; }
  .tele-item i { display: none; }

  /* Chrono en cours : la cible d'arrêt grandit (on est au doigt), le nom rétrécit. */
  .chrono-top { padding: 2px 3px 2px 6px; gap: 5px; }
  .chrono-nom { max-width: 100px; font-size: 10.5px; }
  .chrono-zone:has(.chrono-top:nth-of-type(2)) .chrono-nom { max-width: 66px; }
  .chrono-zone:has(.chrono-top:nth-of-type(3)) .chrono-nom { max-width: 52px; }
  .chrono-compteur { font-size: 11px; min-width: 6.5ch; }
  .chrono-stop { width: 30px; height: 30px; }

  /*
     QUAND UN CHRONO TOURNE, IL PREND LA PLACE.
     Mesuré dans Chrome à 375 px : l'en-tête réclamait 303 px pour 159 disponibles, et
     le bouton d'arrêt tombait à 415 px — hors de l'écran. Faire glisser la bande sous
     le doigt n'est pas une réponse : un bouton d'arrêt qu'il faut aller CHERCHER est
     un chrono qu'on laisse tourner, donc une facture fausse.
     On efface donc ce qui se relit ailleurs, et rien d'autre :
      - les relevés du parc, des alertes et des tickets : ils sont sur le tableau de
        bord et dans les rails du bas. Le compteur de la SEMAINE reste, à côté du
        chrono — c'est ce que Franck a demandé ;
      - le logo DELMAK : décoratif, et le retour à l'accueil est dans les rails ;
      - le bouton « Aller à… » : son raccourci est ⌘K, il n'y a pas de clavier ici.
     Repli si :has() manque (navigateur ancien) : la bande défile, rien n'est perdu. */
  .tele:has(.chrono-top) .tele-item:not(.tele-semaine) { display: none; }
  /* 31/10/2026 : les « rails du bas » n'existent plus, et « Aller à… » est le seul chemin vers
     Facturation, Réglages, Agenda… sans clavier. En Split View sur iPad (≈ 680 px), il y a la place :
     ces quatre boutons ne s'effacent qu'au téléphone (< 640 px). */
  @media (max-width: 639px) {
    body:has(.chrono-top) .brand { display: none; }
    body:has(.chrono-top) .pal-open { display: none; }
  }
  /* Et le bouton « Ticket » : la création rapide se retrouve depuis l'écran Tickets,
     le chrono nulle part ailleurs. C'est ce qui ramène le bouton d'arrêt dans l'écran
     à 320 px (mesuré). */
  @media (max-width: 639px) { body:has(.chrono-top) .top-new { display: none; } }
  /* Le bouton « Mémo » aussi (mesuré : il laissait 3 px au nom du ticket à 375 px).
     Le mémo reste à un geste : bouton « Mémo » de la barre du tableau de bord. */
  @media (max-width: 639px) { body:has(.chrono-top) .top-memo { display: none; } }

  /* Les deux rails fusionnent en une barre basse, défilable, à portée de pouce. */
  .hud-rail, .hud-side {
    position: fixed; top: auto; bottom: 0; left: 0; right: 0;
    transform: none; z-index: 44;
  }
  .hud-side { bottom: 52px; }
  .hud-rail .rail-inner, .hud-side .rail-inner {
    flex-direction: row; border-radius: 0; border-left: 0; border-right: 0; border-bottom: 0;
    padding: 5px 8px; gap: 2px; overflow-x: auto; scrollbar-width: none;
  }
  .hud-rail .rail-inner::-webkit-scrollbar, .hud-side .rail-inner::-webkit-scrollbar { display: none; }
  .hud-side .rail-inner { background: rgba(14, 20, 32, .9); }
  .rail-btn { width: 42px; height: 38px; flex: none; }
  /* Au doigt il n'y a pas de survol : le libellé permanent serait illisible,
     on garde l'icône seule et l'ergot d'état sous le bouton actif. */
  .rail-label { display: none; }
  .hud-rail .rail-btn.active::before, .hud-side .rail-btn.active::before {
    top: auto; right: auto; left: 50%; bottom: -5px; margin: 0 0 0 -8px; width: 16px; height: 3px;
  }
  body.hud-on main.container { padding: 54px 14px 110px; }
  .visor-corner { width: 16px; height: 16px; }
  .visor-ticks { display: none; }
}

/* Le balayage et les pulsations sont un décor : ils disparaissent sur demande. */
@media (prefers-reduced-motion: reduce) { .visor-scan { display: none; } }

/* Écran de connexion : pas de coque, la console n'est pas encore allumée. */
body:not(.hud-on) .auth-wrap { min-height: 100vh; }

/* ============================================================
   PALETTE DE COMMANDE (Ctrl+K)
   ============================================================ */
.pal { position: fixed; inset: 0; z-index: 120; display: flex; justify-content: center; padding: 12vh 16px 16px; background: rgba(4, 7, 13, .74); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.pal[hidden] { display: none; }
.pal-box {
  width: 100%; max-width: 620px; max-height: 74vh; display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border-lit); border-radius: 14px; overflow: hidden;
  box-shadow: 0 40px 100px rgba(0, 0, 0, .75), 0 0 0 1px rgba(53, 208, 242, .16), 0 0 60px rgba(53, 208, 242, .1);
}
.pal-field { display: flex; align-items: center; gap: 10px; padding: 13px 15px; border-bottom: 1px solid var(--border); }
.pal-field .ic { width: 17px; height: 17px; color: var(--accent); flex: none; }
.pal-field input { flex: 1; background: none; border: 0; padding: 0; font: 500 15px var(--font); color: var(--fg); outline: none; }
.pal-field input:focus { box-shadow: none; border: 0; }
.pal-field input::placeholder { color: var(--dim); }

.pal-list { overflow-y: auto; padding: 6px; }
.pal-group { padding: 9px 10px 4px; font-size: 9.5px; text-transform: uppercase; letter-spacing: .14em; color: var(--muted); font-weight: 700; }
.pal-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px; color: var(--fg); font-size: 13.5px; }
.pal-item:hover { text-decoration: none; }
.pal-item .ic { width: 16px; height: 16px; color: var(--muted); flex: none; }
.pal-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pal-hint { color: var(--muted); font-size: 11.5px; max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* La ligne visée s'allume : c'est elle qu'Entrée ouvrira. */
.pal-item.on { background: linear-gradient(90deg, rgba(53, 208, 242, .18), rgba(124, 108, 246, .1)); box-shadow: inset 0 0 0 1px rgba(53, 208, 242, .35); }
.pal-item.on .ic { color: var(--accent); }
.pal-vide { padding: 22px 12px; text-align: center; color: var(--muted); font-size: 13px; margin: 0; }

.pal-foot { display: flex; align-items: center; gap: 14px; padding: 9px 14px; border-top: 1px solid var(--border); font-size: 10.5px; color: var(--muted); flex-wrap: wrap; }
kbd { display: inline-block; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--border-lit); background: rgba(255, 255, 255, .05); font-family: var(--font-num); font-size: 10px; color: var(--fg); }

/* Déclencheur dans le bandeau. */
/* Bouton d'action de la barre du haut : contrairement à « Aller à… », qui est un
   outil de navigation en gris, celui-ci CRÉE quelque chose — il porte donc l'accent.
   Il reste discret : c'est un liseré teinté, pas un aplat. */
.top-new {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 10px; border-radius: 6px;
  background: var(--accent-soft); border: 1px solid rgba(53, 208, 242, .38);
  color: var(--accent); font: 600 11.5px var(--font); white-space: nowrap;
}
.top-new:hover { border-color: var(--accent); box-shadow: var(--glow); text-decoration: none; }
.top-new .ic { width: 13px; height: 13px; }
/* Le « + » est dessiné en CSS : pas d'icône supplémentaire à ajouter au sprite. */
.top-new::before { content: '+'; font: 700 14px/1 var(--font-num); margin-right: -2px; }
/* Sous 900 px, seule l'icône reste — exactement comme « Aller à… » juste à côté. */
@media (max-width: 899px) { .top-new span { display: none; } .top-new { padding: 5px 7px; } }

.pal-open {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 9px; border-radius: 6px; cursor: pointer;
  background: rgba(255, 255, 255, .03); border: 1px solid var(--border);
  color: var(--muted); font: 500 11.5px var(--font);
}
.pal-open:hover { border-color: var(--accent); color: var(--accent); }
.pal-open .ic { width: 13px; height: 13px; }
@media (max-width: 899px) { .pal-open span, .pal-open kbd { display: none; } .pal-open { padding: 5px; } .pal-foot { display: none; } .pal { padding-top: 8vh; } }

/* Bouton « Mémo » (⌘M) : un outil, habillé comme « Aller à… ». Aucun des deux ne
   passe sur deux lignes quand le bandeau se remplit : c'est le nom de l'agence, à
   droite, qui se tronque (ellipse). */
.pal-open, .top-memo { flex: none; white-space: nowrap; }
.top-memo {
  position: relative;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 9px; border-radius: 6px; cursor: pointer;
  background: rgba(255, 255, 255, .03); border: 1px solid var(--border);
  color: var(--muted); font: 500 11.5px var(--font);
}
.top-memo:hover, .top-memo[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
/* Effets sonores (30/09/2026) : bouton haut-parleur, icône seule. Coupés = grisé. */
.top-son {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 26px; padding: 0; border-radius: 6px; cursor: pointer;
  background: rgba(255, 255, 255, .03); border: 1px solid var(--border); color: var(--muted);
}
.top-son:hover { border-color: var(--accent); color: var(--accent); }
.top-son[aria-pressed="true"] { color: var(--accent); }
.top-son[aria-pressed="false"] { color: var(--dim); }
.top-son .ic { width: 15px; height: 15px; }
/* Sous 900 px, la commande « Sons » de ⌘K suffit : le bandeau n'a pas la place. Placé APRÈS
   la règle du bouton (même spécificité : la dernière l'emporte). */
@media (max-width: 899px) { .top-son { display: none; } }
.top-memo .ic { width: 13px; height: 13px; }
/* Brouillon non enregistré retrouvé sur cet appareil : le message attend dans la
   modale fermée, une pastille le signale. */
.top-memo.memo-a-voir::after {
  content: ''; position: absolute; top: -3px; right: -3px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--warn); box-shadow: 0 0 0 2px var(--bg), 0 0 8px rgba(251, 191, 36, .6);
}
/* Sous 1440 px, l'icône seule (le titre dit « Mémo (⌘M) ») : avec son libellé, le
   bouton poussait la bande des relevés, qui rognait « Chrono » à 1280 px (mesuré). */
@media (max-width: 1439px) { .top-memo span, .top-memo kbd { display: none; } .top-memo { padding: 5px 7px; } }
/* Bouton « Agenda » (⌘A, 28/09/2026) : même habit que « Mémo ». Libellé masqué sous
   1440 px ; bouton MASQUÉ sous 900 px — il ferait déborder le bandeau à 320 px. Sur
   téléphone, l'agenda s'ouvre par le bandeau « Prochain rendez-vous » et l'encart. */
.top-agenda {
  flex: none; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 9px; border-radius: 6px; cursor: pointer;
  background: rgba(255, 255, 255, .03); border: 1px solid var(--border);
  color: var(--muted); font: 500 11.5px var(--font);
}
.top-agenda:hover, .top-agenda[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.top-agenda .ic { width: 13px; height: 13px; }
@media (max-width: 1439px) { .top-agenda span, .top-agenda kbd { display: none; } .top-agenda { padding: 5px 7px; } }
@media (max-width: 899px) { .top-agenda { display: none; } }
/* Second accès, dans la barre du tableau de bord : sur téléphone seulement, là où le
   bandeau peut manquer de place (chrono en cours). */
/* Bouton Mémo du tableau de bord : seul bouton du mémo depuis le 05/10/2026 (celui du bandeau est
   retiré), donc montré à toutes les largeurs, avec la pastille du brouillon retrouvé. */
.dash-memo-bt { position: relative; display: inline-flex; align-items: center; gap: 6px; }
.dash-memo-bt .ic { width: 13px; height: 13px; }
.dash-memo-bt.memo-a-voir::after {
  content: ''; position: absolute; top: -3px; right: -3px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--warn); box-shadow: 0 0 0 2px var(--bg), 0 0 8px rgba(251, 191, 36, .6);
}
/* Et sous 1100 px, « Aller à… » perd son rappel ⌘K (toujours dans le titre) : c'est la
   place que le bouton Mémo a prise, la semaine « S39 » reste lisible (mesuré à 1000 px). */
@media (max-width: 1099px) { .pal-open kbd { display: none; } }

/* ============================================================
   MODALES — DELMAK n'a qu'un écran
   Tout écran interne est chargé en fragment et affiché ici.
   La boîte est grande : elle accueille des tableaux entiers,
   pas seulement une fiche.
   ============================================================ */
.mdl {
  position: fixed; inset: 0; z-index: 110;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 5vh 16px 16px;
  background: rgba(4, 7, 13, .74);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
}
.mdl[hidden] { display: none; }
/* La page dessous ne défile plus : sinon on perd sa position en fermant. */
body.mdl-ouverte { overflow: hidden; }

.mdl-box {
  width: 100%; max-width: 1120px; max-height: 90vh;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border-lit); border-radius: 14px; overflow: hidden;
  box-shadow: 0 40px 110px rgba(0, 0, 0, .78), 0 0 0 1px rgba(53, 208, 242, .14);
}
.mdl-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); }
/* min-width:0 et overflow-wrap : un titre fait d'un long mot insécable (libellé de tâche cron
   « Synchronisation_du_… ») se replie au lieu de pousser ✕ hors de l'écran (élément flex). */
.mdl-title { margin: 0; min-width: 0; overflow-wrap: anywhere; font-family: var(--font-brand); font-size: 13px; text-transform: uppercase; letter-spacing: .13em; }
.mdl-title::before { content: ''; display: inline-block; width: 3px; height: 13px; border-radius: 2px; margin-right: 9px; vertical-align: -2px; background: linear-gradient(180deg, var(--accent), var(--accent-2)); box-shadow: 0 0 10px rgba(53, 208, 242, .55); }
.mdl-nav {
  width: 30px; height: 30px; flex: none; border-radius: 7px; cursor: pointer;
  background: rgba(255, 255, 255, .04); border: 1px solid var(--border-lit); color: var(--muted);
  font-size: 15px; line-height: 1; display: flex; align-items: center; justify-content: center;
}
.mdl-nav:hover { border-color: var(--accent); color: var(--accent); }
/* display:flex ci-dessus annule l'attribut hidden : sans cette règle, ‹ restait
   affiché à la racine de la pile, où un clic fermait la modale. */
.mdl-nav[hidden] { display: none; }
.mdl-body { overflow-y: auto; padding: 18px; }
/* Le contenu injecté est un écran entier : son premier titre fait doublon
   avec celui de la modale, et sa marge du haut déséquilibre la boîte. */
.mdl-body .frag > .toolbar:first-child h1 { font-size: 17px; }
.mdl-body .card:last-child { margin-bottom: 0; }
.mdl-erreur { color: var(--danger); text-align: center; padding: 30px 10px; }

/* Attente : trois points, pas un tourniquet — on ne fait pas patienter longtemps. */
.mdl-attente { display: flex; gap: 7px; justify-content: center; padding: 48px 0; }
.mdl-attente span { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); opacity: .3; animation: mdlPoint 1.1s ease-in-out infinite; }
.mdl-attente span:nth-child(2) { animation-delay: .18s; }
.mdl-attente span:nth-child(3) { animation-delay: .36s; }
@keyframes mdlPoint { 0%, 100% { opacity: .25; transform: scale(.85); } 50% { opacity: 1; transform: scale(1); } }

@media (max-width: 899px) {
  .mdl { padding: 0; }
  .mdl-box { max-width: none; max-height: 100vh; height: 100vh; max-height: 100dvh; height: 100dvh; border-radius: 0; border: 0; }
  .mdl-body { padding: 14px; }
}

/* Le contenu occupe toute la largeur : plus de rails à contourner. */
body.hud-on main.container { padding-left: 20px; padding-right: 20px; }
@media (min-width: 900px) { body.hud-on main.container { padding-left: 24px; padding-right: 24px; } }

/* --- Nouveaux panneaux du dashboard --- */
/* Placement : voir « Dashboard : grille des encarts » (largeurs réglables). */
.dash-parc .table-wrap { border: 0; border-radius: 0; }
.dash-parc th:first-child, .dash-parc td:first-child { padding-left: 0; }
.dash-parc td { font-size: 13px; }

/* --- Écran « Hash mot de passe PrestaShop » (palette ⌘K, encart du tableau de bord jusqu'au
   28/09/2026) --- */
/* Ses champs se règlent sur la largeur de LA CARTE (container encart, plus haut). */

/* Champs : empilés sur téléphone ; côte à côte dès 640 px, bouton calé en pied. */
.psh-champs { display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr); align-items: end; }
.psh-champs .field { margin-bottom: 0; min-width: 0; }
@container encart (min-width: 562px) { .psh-champs { grid-template-columns: minmax(0, 1fr) auto; } }
/* `.mono` seule perd contre `input[type=text]`, qui pose sa police en raccourci. */
.psh-champs input.mono { font-family: var(--font-num); }
/* 16 px au doigt : en dessous, iOS zoome la page à la mise au point du champ. */
@media (pointer: coarse) { .psh-champs input { font-size: 16px; } }

.psh-mdp { display: flex; gap: 6px; }
.psh-mdp input { flex: 1 1 auto; min-width: 0; }
/* Largeur fixe : « Afficher » ↔ « Masquer » ne fait pas sauter le champ. */
.psh-oeil {
  flex: none; min-width: 5.8em; padding: 0 10px; cursor: pointer;
  background: var(--bg-2); color: var(--muted);
  border: 1px solid var(--border-lit); border-radius: 10px;
  font: 600 10.5px var(--font); letter-spacing: .08em; text-transform: uppercase;
}
.psh-oeil:hover { border-color: var(--accent); color: var(--accent); }
.psh-oeil:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Même hauteur que les champs voisins (≈ 42 px) ; pleine largeur sous 640 px. */
.psh-go { min-height: 42px; }
@media (max-width: 639px) { .psh-go { width: 100%; } }
/* Calcul en cours : aria-disabled et non disabled (le focus clavier reste sur le bouton). */
.btn.psh-go[aria-disabled="true"] { opacity: .55; cursor: progress; filter: grayscale(.4); box-shadow: none; }

/* Messages. La région d'alerte n'est JAMAIS en display:none : une région live
   démasquée au moment où le message arrive n'est pas lue par tous les lecteurs
   d'écran. Vide, elle n'a déjà aucune hauteur ; seule sa marge est conditionnelle.
   Le bloc d'avertissements, lui, n'est pas une région live : vide, il sort de la
   grille, sinon il y laisserait un espacement fantôme. */
.psh-erreurs:not(:empty) { margin-top: 10px; }
.psh-avert:empty { display: none; }
.psh-msg {
  margin: 0; padding: 8px 10px; font-size: 12.5px;
  border: 1px solid; border-radius: var(--radius-sm);
}
.psh-msg + .psh-msg { margin-top: 6px; }
.psh-msg.bad { color: var(--danger); border-color: rgba(251, 95, 122, .45); background: rgba(251, 95, 122, .08); }
.psh-msg.warn { color: var(--warn); border-color: rgba(251, 191, 36, .45); background: rgba(251, 191, 36, .08); }

/* Résultat. display posé : [hidden] rétabli juste après. */
.psh-res { display: grid; gap: 10px; margin-top: 14px; }
.psh-res[hidden] { display: none; }
.psh-ligne {
  display: grid; gap: 6px 16px; grid-template-columns: minmax(0, 1fr);
  padding: 10px 12px; min-width: 0;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
/* Colonne d'intitulé bornée à 280 px : à 1000 px d'écran, un bcrypt (60 caractères)
   tient encore sur une ligne à côté du bouton « Copier ». */
@media (min-width: 900px) { .psh-ligne { grid-template-columns: minmax(210px, 280px) minmax(0, 1fr); align-items: center; } }
.psh-lib { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.psh-lib strong { font-size: 13px; }
.psh-algo { font-weight: 400; color: var(--muted); }
.psh-val { display: flex; align-items: center; gap: 8px; min-width: 0; }
.psh-val[hidden] { display: none; }
/* Le hash : coupé n'importe où (aucune espace dedans), et sélectionné d'un seul clic. */
.psh-hash {
  flex: 1 1 auto; min-width: 0;
  padding: 7px 9px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg);
  font-family: var(--font-num); font-size: 12.5px; line-height: 1.45; color: var(--fg);
  overflow-wrap: anywhere; word-break: break-all;
  -webkit-user-select: all; user-select: all; cursor: text;
}
.psh-copier { flex: none; }
@media (pointer: coarse) { .psh-copier { min-height: 40px; } }
.psh-pied code { font-family: var(--font-num); font-size: 11.5px; color: var(--fg); overflow-wrap: anywhere; }
.psh-pied { margin: 12px 0 0; }

/* ============================================================
   TICKETS — groupes de priorité et classement à la main
   (glisser la poignée ⋮⋮, ou ⌥↑ / ⌥↓ sur le lien du ticket).
   En fin de feuille : les règles « plaque d'identification » des
   cartes (.tk-card:hover…) sont plus haut et ne doivent pas
   l'emporter sur la carte en cours de glisser.
   ============================================================ */
.tk-groupe-titre { font-size: 12px; margin: 16px 0 8px; letter-spacing: .1em; }
.tk-groupe:first-of-type .tk-groupe-titre { margin-top: 4px; }
/* Le voyant du titre prend la couleur du groupe, comme le liseré des cartes. */
.tk-groupe.p3 .tk-groupe-titre::before { background: var(--danger); box-shadow: 0 0 10px rgba(251, 95, 122, .55); }
.tk-groupe.p2 .tk-groupe-titre::before { background: var(--warn); box-shadow: 0 0 10px rgba(251, 191, 36, .5); }
.tk-groupe-nb { color: var(--muted); font-family: var(--font-num); font-weight: 500; letter-spacing: 0; }
/* Repère des positions absolues : le trait d'insertion est posé dans la grille. */
.tk-grid { position: relative; }
/* Message d'échec COLLANT : on glisse souvent tout en bas de la liste, un message
   posé en tête serait hors de vue. Fond opaque sous la teinte d'erreur, sinon les
   cartes transparaîtraient ; en page pleine, sous la barre fixe du HUD (46 px). */
.flash.tk-ordre-msg {
  position: sticky; top: 0; z-index: 25; margin: 4px 0 10px;
  background: linear-gradient(rgba(251, 95, 122, .12), rgba(251, 95, 122, .12)), var(--card);
}
body:not(.mdl-ouverte) .flash.tk-ordre-msg { top: 54px; }

/* Poignée : au-dessus du lien étiré (z-index 1), sans agrandir l'en-tête (marges
   négatives). touch-action:none — le doigt déplace la carte au lieu de faire
   défiler, condition du glisser tactile en Pointer Events. */
.tk-poignee {
  position: relative; z-index: 2; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; margin: -8px -4px -8px -9px; padding: 0;
  border: 0; border-radius: 6px; background: transparent; color: var(--muted);
  font-size: 13px; line-height: 1; letter-spacing: -2px;
  cursor: grab; touch-action: none;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.tk-poignee:hover { color: var(--accent); background: rgba(53, 208, 242, .08); }
.tk-poignee[hidden] { display: none; }
/* Doigt : cible de 40 px, toujours sans grandir la carte. */
@media (pointer: coarse) {
  .tk-poignee { width: 40px; height: 40px; margin: -13px -6px -13px -11px; font-size: 16px; }
}

/* Carte en cours de glisser : décalée par transform (sa place reste marquée par le
   vide qu'elle laisse), sans transition, au-dessus de ses voisines. */
.tk-card.tk-glisse {
  z-index: 20; transition: none; cursor: grabbing; opacity: .94;
  border-color: var(--accent);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .55), 0 0 0 1px rgba(53, 208, 242, .35);
}
body.tk-glisse-en-cours, body.tk-glisse-en-cours * { cursor: grabbing; -webkit-user-select: none; user-select: none; }

/* Trait d'insertion : vertical entre deux cartes d'une rangée, horizontal sur
   une seule colonne (téléphone). display posé ici : [hidden] rétabli juste après. */
.tk-repere {
  position: absolute; z-index: 15; display: block; pointer-events: none;
  border-radius: 3px; background: var(--accent); box-shadow: 0 0 12px rgba(53, 208, 242, .8);
}
.tk-repere[hidden] { display: none; }
/* Changer de PRIORITÉ en glissant (28/09/2026) : un groupe vide n'apparaît que pendant un
   glisser, avec sa zone de dépôt ; le groupe visé s'éclaire. */
.tk-groupe-vide { display: none; }
body.tk-glisse-en-cours .tk-groupe-vide { display: block; }
.tk-vide-cible { margin: 0; padding: 14px; border: 1px dashed var(--border-lit); border-radius: 10px; color: var(--muted); font-size: 12px; text-align: center; }
.tk-groupe.tk-groupe-cible .tk-vide-cible { border-color: var(--accent); color: var(--fg); background: rgba(53, 208, 242, .06); }
.tk-groupe.tk-groupe-cible .tk-groupe-titre { color: var(--accent); }

/* Jamais reclassé à la main : suit les classés, par ancienneté. Discret. */
.tk-nonclasse {
  font-size: 10px; line-height: 1.5; padding: 0 6px; border-radius: 999px; white-space: nowrap;
  border: 1px dashed var(--border-lit); color: var(--muted);
}

/* ---------- Tâches cron : titre, état, boutons (partagés avec webcron/*.html.twig) ---------- */
.wc-titre { display: flex; align-items: center; gap: 8px; }
.wc-etat { margin: 6px 0 0; font-size: 12.5px; color: var(--fg); }
.wc-etat .muted { font-size: 11.5px; }
.wc-boutons { display: flex; flex-wrap: wrap; gap: 6px; }
@media (min-width: 640px) {
  .wc-boutons { flex: none; }
}

/* ---------- Canaux d'alerte (Alertes → Canaux) ----------
   Une carte par canal, au doigt d'abord : destination et erreurs passent à la ligne
   n'importe où plutôt que d'élargir la page. Réglages et journal repliés. */
.chn-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.chn-tete form { display: inline; }
.chn-tete .badge[type=submit] { font: inherit; font-size: 10.5px; }
.chn-dest { margin-top: 8px; color: var(--fg); overflow-wrap: anywhere; word-break: break-all; }
.chn-regles { margin-top: 4px; line-height: 1.6; }
.chn-etat { margin-top: 8px; display: flex; flex-direction: column; gap: 2px; }
.chn-erreur { color: var(--danger); overflow-wrap: anywhere; }
.chn-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.chn-replie { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
.chn-replie summary { cursor: pointer; color: var(--muted); font-size: 12.5px; }
.chn-replie summary:hover { color: var(--accent); }
.chn-replie[open] summary { margin-bottom: 10px; }
.chn-types { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 6px; }
.chn-case { display: flex; align-items: center; gap: 8px; text-transform: none; letter-spacing: 0; font-size: 13px; font-weight: 400; color: var(--fg); }
.chn-envois { list-style: none; margin: 0; padding: 0; }
.chn-envoi { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; padding: 8px 0; border-top: 1px solid var(--border); font-size: 12.5px; }
.chn-envoi:first-child { border-top: 0; }
.chn-objet { flex: 1 1 100%; min-width: 0; overflow-wrap: anywhere; }
@media (min-width: 640px) {
  .chn-objet { flex: 1 1 0; }
}

/* ============================================================
   Disponibilité (lot B du 19/09) : écran, fiche d'un site, réglages.
   Mobile d'abord : cartes sous 640 px, grille-tableau au-delà ; la colonne
   « dernier incident » passe sous le nom entre 640 et 999 px.
   ============================================================ */
a.tele-item:hover { text-decoration: none; border-color: var(--accent); }
.bad-txt { color: var(--danger); }
.ok-txt { color: var(--success); }
.dsp-pct b.ok, .dsp-parc b.ok, .dsp-tuile.ok b { color: var(--success); }
.dsp-pct b.warn, .dsp-parc b.warn, .dsp-tuile.warn b { color: var(--warn); }
.dsp-pct b.bad, .dsp-parc b.bad, .dsp-tuile.bad b { color: var(--danger); }
.dsp-pct b.vide, .dsp-parc b.vide, .dsp-tuile.vide b { color: var(--muted); }
.dsp-bandeau a { color: inherit; text-decoration: underline; }
/* Bandeau « dernier envoi en échec » : une erreur de transport sans espace (URL, jeton)
   passe à la ligne au lieu d'être coupée par l'overflow:hidden de .flash (téléphone). */
.dsp-bandeau { overflow-wrap: anywhere; }
.site-metrics a.m { display: block; color: inherit; }
.site-metrics a.m:hover { text-decoration: none; border-color: var(--accent); }

/* En-tête : parc et fraîcheur de la sonde */
.dsp-entete { padding: 14px 16px; }
.dsp-synthese { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; }
.dsp-compte { display: flex; align-items: baseline; gap: 2px; }
.dsp-compte b { font-size: 30px; line-height: 1; font-weight: 700; }
.dsp-compte i { font-style: normal; font-size: 16px; color: var(--muted); }
.dsp-compte span { margin-left: 8px; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .12em; font-weight: 600; }
.dsp-compte.ok b { color: var(--success); }
.dsp-compte.warn b { color: var(--warn); }
.dsp-compte.bad b { color: var(--danger); }
/* Téléphone : compteur et parc 30 j sur la 1re ligne, états en dessous sur toute la largeur. */
.dsp-etats { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; order: 3; flex: 1 1 100%; }
@media (min-width: 640px) { .dsp-etats { order: 0; flex: 1 1 200px; } }
.dsp-parc { display: flex; flex-direction: column; align-items: flex-end; margin-left: auto; }
.dsp-parc span { font-size: 10.5px; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); font-weight: 600; }
.dsp-parc b { font-size: 20px; }
.dsp-fraicheur { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 12px 0 0; padding-top: 10px; border-top: 1px solid var(--border); font-size: 12.5px; color: var(--muted); }
.dsp-fraicheur.warn { color: var(--warn); }
.dsp-fraicheur.bad { color: var(--danger); }
.dsp-fraicheur strong { color: inherit; }

/* Liste : une carte par site (téléphone) */
.dsp-liste { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.dsp-tete { display: none; }
.dsp-ligne {
  display: grid; gap: 8px 10px; grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-areas: "site site site" "etat etat etat" "p24 p7 p30" "temps temps temps" "dern dern dern";
  padding: 12px 14px; color: inherit;
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color .12s ease, background .12s ease;
}
.dsp-ligne:hover { text-decoration: none; border-color: var(--accent); }
.dsp-ligne:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dsp-ligne.eteinte { opacity: .62; }
.dsp-site { grid-area: site; display: flex; align-items: center; gap: 9px; min-width: 0; }
.dsp-nom { display: flex; flex-direction: column; min-width: 0; }
/* Le nom peut tenir sur deux lignes : en capitales il est plus large que la
   colonne à 375 et à 768 px, et le rogner effacerait ce qui distingue deux
   boutiques d'un même client (« … de Brest », « … Quimper »). */
.dsp-nom strong { overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.dsp-nom small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dsp-nom small, .dsp-etat small { color: var(--muted); font-size: 12px; }
.dsp-etat { grid-area: etat; display: flex; flex-direction: column; gap: 3px; min-width: 0; overflow-wrap: anywhere; }
.dsp-etat small.warn-txt { color: var(--warn); }
.dsp-badges { display: flex; flex-wrap: wrap; gap: 5px; }
.dsp-pct { display: flex; flex-direction: column; padding: 6px 8px; background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.dsp-p24 { grid-area: p24; } .dsp-p7 { grid-area: p7; } .dsp-p30 { grid-area: p30; }
.dsp-l { font-style: normal; font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; font-weight: 600; }
.dsp-pct b { font-size: 14px; }
.dsp-temps { grid-area: temps; display: flex; align-items: center; gap: 10px; min-width: 0; }
.dsp-temps b { white-space: nowrap; font-size: 13px; }
.dsp-spark { flex: 1; min-width: 0; max-width: 240px; }
.dsp-spark .spark { height: 22px; min-width: 60px; }
.dsp-spark .spark-bornes { display: none; }
.dsp-dernier { grid-area: dern; overflow-wrap: anywhere; }

@media (min-width: 640px) {
  .dsp-liste { gap: 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
  .dsp-tete, .dsp-ligne {
    display: grid; column-gap: 14px;
    /* Colonne « temps » : 140 px au moins = valeur en nowrap (≈ 62 px) + écart de 10 px
       + petite courbe (min-width 60 px) ; à 110 px, la courbe était rognée de 10 px par
       l'overflow:hidden de .dsp-liste entre 640 et ~800 px. */
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1.5fr) 70px 70px 70px minmax(140px, 1fr);
    grid-template-areas: "site etat p24 p7 p30 temps" "dern etat p24 p7 p30 temps";
  }
  .dsp-tete {
    grid-template-areas: "site etat p24 p7 p30 temps";
    padding: 10px 14px; background: rgba(18, 26, 43, .8); border-bottom: 1px solid var(--border-lit);
    color: var(--muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: .12em; font-weight: 700; white-space: nowrap;
  }
  .dsp-tete > :nth-child(1) { grid-area: site; } .dsp-tete > :nth-child(2) { grid-area: etat; }
  .dsp-tete > :nth-child(3) { grid-area: p24; } .dsp-tete > :nth-child(4) { grid-area: p7; }
  .dsp-tete > :nth-child(5) { grid-area: p30; } .dsp-tete > :nth-child(6) { grid-area: temps; }
  .dsp-tete-dernier { display: none; }
  .dsp-c { text-align: right; }
  .dsp-ligne { row-gap: 2px; align-items: center; padding: 10px 14px; border: 0; border-top: 1px solid var(--border); border-radius: 0; background: transparent; }
  .dsp-ligne:hover { background: rgba(53, 208, 242, .07); box-shadow: inset 2px 0 0 var(--accent); }
  .dsp-ligne:focus-visible { outline-offset: -2px; }
  .dsp-site { align-self: end; }
  .dsp-dernier { align-self: start; padding-left: 17px; }
  .dsp-pct { padding: 0; background: none; border: 0; align-items: flex-end; }
  /* Libellés des cellules : l'en-tête les porte ; gardés pour les lecteurs d'écran. */
  .dsp-l { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  .dsp-ligne { position: relative; }
}
@media (min-width: 1000px) {
  .dsp-tete, .dsp-ligne {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.4fr) 68px 68px 68px minmax(140px, .9fr) minmax(0, 1.15fr);
    grid-template-areas: "site etat p24 p7 p30 temps dern";
  }
  .dsp-tete > :nth-child(7) { grid-area: dern; }
  .dsp-tete-dernier { display: block; }
  .dsp-site, .dsp-dernier { align-self: center; }
  .dsp-dernier { padding-left: 0; }
}

/* Journal d'incidents */
.dsp-journal { list-style: none; margin: 0; padding: 0; }
.dsp-inc { padding: 10px 0 10px 12px; border-top: 1px solid var(--border); border-left: 2px solid transparent; }
.dsp-inc:first-child { border-top: 0; }
.dsp-inc.bad { border-left-color: var(--danger); }
.dsp-inc.warn { border-left-color: var(--warn); }
.dsp-inc-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; margin-bottom: 3px; }
.dsp-inc-cause { overflow-wrap: anywhere; }
.dsp-inc-mails { margin-top: 2px; }
.dsp-replie { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.dsp-replie summary { cursor: pointer; color: var(--muted); font-size: 12.5px; padding: 6px 0; }
.dsp-replie summary:hover { color: var(--accent); }

/* Fiche d'un site */
.dsp-fiche-titre { min-width: 0; }
.dsp-fiche-titre h1 { display: flex; align-items: center; gap: 10px; }
.dsp-fiche-titre .page-sub { overflow-wrap: anywhere; }
.dsp-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 14px; }
.dsp-actions form { display: inline-flex; margin: 0; }
.dsp-fiche-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.dsp-controles { display: grid; grid-template-columns: 1fr; gap: 10px; margin: 12px 0 0; }
.dsp-controles dt { font-size: 10.5px; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); font-weight: 600; }
.dsp-controles dd { margin: 2px 0 0; overflow-wrap: anywhere; }
.dsp-tuiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 16px; }
.dsp-tuile { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: linear-gradient(180deg, rgba(255, 255, 255, .03), transparent), var(--bg-2); }
.dsp-tuile-lib { font-size: 10px; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); font-weight: 600; }
.dsp-tuile b { font-size: 20px; line-height: 1.25; }
.dsp-test { border-left: 3px solid var(--border-lit); }
.dsp-test.ok { border-left-color: var(--success); }
.dsp-test.warn { border-left-color: var(--warn); }
.dsp-test.bad { border-left-color: var(--danger); }
.dsp-test p { overflow-wrap: anywhere; }
@media (min-width: 640px) {
  .dsp-controles { grid-template-columns: 1fr 1fr; }
  .dsp-tuiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Courbe des temps de réponse (SVG rendu côté serveur, graduations en HTML) */
.dsp-courbe-tete { margin-bottom: 6px; }
.dsp-onglets { display: flex; gap: 6px; }
.dsp-courbe-resume { margin: 0 0 10px; }
.dsp-courbe { display: flex; gap: 6px; }
.dsp-courbe-y { position: relative; flex: none; width: 42px; height: 170px; font-family: var(--font-num); font-size: 10px; color: var(--muted); }
.dsp-courbe-y span { position: absolute; right: 0; transform: translateY(-50%); white-space: nowrap; }
.dsp-courbe-zone { position: relative; flex: 1; min-width: 0; }
.dsp-courbe-zone svg { display: block; width: 100%; height: 170px; overflow: visible; }
.dsp-courbe-zone .grille { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 4; }
.dsp-courbe-zone .grille.base { stroke: var(--border-lit); stroke-dasharray: none; }
.dsp-courbe-zone .panne { fill: rgba(251, 95, 122, .2); }
.dsp-courbe-zone path.moy { stroke: var(--accent); stroke-width: 1.6; }
.dsp-courbe-zone path.p95 { stroke: var(--accent-2); stroke-width: 1.2; stroke-dasharray: 4 3; }
.dsp-courbe-x { position: relative; height: 20px; font-size: 10px; color: var(--muted); }
.dsp-courbe-x span { position: absolute; top: 5px; transform: translateX(-50%); white-space: nowrap; }
.dsp-courbe-vide { padding: 30px 0; text-align: center; color: var(--muted); font-size: 13px; }
.dsp-legende { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 6px 0 0; }
.dsp-legende i { display: inline-block; width: 14px; height: 3px; margin-right: 6px; vertical-align: middle; border-radius: 2px; }
.dsp-legende i.moy { background: var(--accent); }
.dsp-legende i.p95 { background: var(--accent-2); }
.dsp-legende i.panne { height: 10px; background: rgba(251, 95, 122, .4); }
@media (max-width: 639px) {
  /* Un libellé sur deux : sinon ils se chevauchent sur 300 px. */
  .dsp-courbe-x span:nth-child(even) { display: none; }
}

/* 90 barres journalières (30 sur téléphone, sauf « Voir les 90 jours ») */
.dsp-barres { display: grid; grid-template-columns: repeat(30, minmax(0, 1fr)); gap: 2px; height: 34px; }
.dsp-barres .b { display: block; border-radius: 2px; background: var(--success); opacity: .85; transition: opacity .1s ease; }
.dsp-barres .b.warn { background: var(--warn); }
.dsp-barres .b.bad { background: var(--danger); }
.dsp-barres .b.vide { background: var(--border-lit); opacity: .55; }
.dsp-barres .b.auj { box-shadow: inset 0 -3px 0 rgba(255, 255, 255, .35); }
.dsp-barres .b:hover { opacity: 1; }
.dsp-barres:not(.tout) .b:nth-child(-n+60) { display: none; }
.dsp-barres.tout { height: auto; grid-auto-rows: 26px; row-gap: 6px; }
.dsp-barres-bornes { display: flex; margin-top: 4px; font-size: 11px; }
.dsp-barres-bornes .loin { display: none; }
.dsp-barres.tout + .dsp-barres-bornes .loin { display: inline; }
.dsp-barres.tout + .dsp-barres-bornes .pres { display: none; }
.dsp-barres-resume { margin: 6px 0 0; }
.dsp-barres-90 { display: inline-block; margin-left: 4px; padding: 6px 0; }
.dsp-jours-mauvais { list-style: none; margin: 8px 0 0; padding: 0; color: var(--muted); }
.dsp-jours-mauvais li { display: flex; align-items: center; gap: 8px; padding: 3px 0; overflow-wrap: anywhere; }
@media (min-width: 640px) {
  .dsp-barres, .dsp-barres.tout { grid-template-columns: repeat(90, minmax(0, 1fr)); height: 38px; grid-auto-rows: auto; row-gap: 0; }
  .dsp-barres:not(.tout) .b:nth-child(-n+60) { display: block; }
  .dsp-barres-bornes .loin, .dsp-barres.tout + .dsp-barres-bornes .loin { display: inline; }
  .dsp-barres-bornes .pres { display: none; }
  .dsp-barres-90 { display: none; }
}

/* Réglages de la sonde (une colonne) */
.dsp-reglages { position: relative; max-width: 680px; }
.dsp-case { display: flex; align-items: flex-start; gap: 9px; margin: 8px 0 0; font-size: 13px; color: var(--fg); font-weight: 400; text-transform: none; letter-spacing: 0; cursor: pointer; }
.dsp-case input { flex: none; margin-top: 2px; }
.dsp-cases .dsp-case { min-height: 44px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-2); }
.dsp-reglages .dsp-actions { margin: 6px 0 0; }
.dsp-reglages input.invalide, .dsp-reglages select.invalide { border-color: var(--danger); }
/* Bouton par défaut (Entrée = Enregistrer), hors écran mais rendu : Safari n'active pas un bouton masqué. */
.dsp-defaut { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Alias historique : plusieurs écrans admin (audit, dérives) écrivent `.badge.danger`
   là où la palette dit `.badge.bad`. Sans cet alias, le badge « Critique » sortait gris.
   Ajouté par le lot « onglet Sécurité » de l'espace client. */
.badge.danger { color: var(--danger); border-color: rgba(251, 95, 122, .45); background: rgba(251, 95, 122, .1); box-shadow: inset 0 0 12px rgba(251, 95, 122, .12); }

/* ==========================================================================
   Note secrète (lot du 22/09/2026)

   Trois écrans partagent ce bloc : l'encart du tableau de bord (admin), l'écran
   /dashboard/note-secrete, l'encart et la page de l'espace client — et la page
   PUBLIQUE d'ouverture, qui n'a que app.css (gabarit autonome, aucune police
   distante, aucun script). Préfixe `ns-`, rien d'autre n'est touché.
   Mobile d'abord : tout est en colonne, et les cibles tactiles font 44 px.
   ========================================================================== */

/* ---------- Formulaire et encarts (admin + espace) ---------- */
.ns-bloc { margin-bottom: 14px; }
.ns-intro { color: var(--muted); font-size: 13px; margin: 0 0 12px; }
.ns-form { display: flex; flex-direction: column; gap: 10px; }
.ns-form label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); margin-bottom: 4px; }
/* Chasse fixe : on y écrit des mots de passe. Un l et un 1 doivent se distinguer
   AVANT l'envoi — c'est la seule relecture possible, le texte disparaît ensuite. */
.ns-zone {
  width: 100%; resize: vertical; min-height: 88px;
  font-family: var(--font-num); font-size: 14px; line-height: 1.5;
  background: var(--bg-2); color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px;
}
.ns-zone:focus { outline: none; border-color: var(--border-lit); box-shadow: var(--glow); }
.ns-form-bas { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; }
.ns-form-duree { flex: 1 1 140px; min-width: 0; }
.ns-form-duree select { width: 100%; min-height: 44px; }
.ns-form-bas .btn, .ns-form-bas .btn-sm { min-height: 44px; flex: 0 0 auto; }
.ns-aide { color: var(--muted); font-size: 12px; line-height: 1.5; margin: 6px 0 0; }

/* ---------- Lien fraîchement créé ---------- */
.ns-lien {
  border: 1px solid rgba(53, 208, 242, .4); background: var(--accent-soft);
  border-radius: var(--radius-sm); padding: 12px 14px;
}
.ns-lien-titre { margin: 0 0 8px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em; color: var(--accent); }
/* Le lien, en entier et sélectionnable d'un seul appui : même recette que
   `.ns-secret`. Dans un <input>, 25 à 35 caractères sur 82 étaient visibles au
   téléphone et il fallait trois gestes pour le copier — or c'est la seule et unique
   fois qu'il s'affiche. `all` sélectionne le bloc entier au premier appui. */
.ns-lien-champ {
  display: block; width: 100%; min-height: 44px; padding: 10px 12px;
  font-family: var(--font-num); font-size: 13px; line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: normal;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--border-lit); border-radius: var(--radius-sm);
  user-select: all; -webkit-user-select: all;
}

/* ---------- Suivi ---------- */
.ns-liste { list-style: none; margin: 0; padding: 0; }
.ns-liste-l { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 10px 0; border-top: 1px solid var(--border); }
.ns-liste-l:first-child { border-top: 0; }
.ns-liste-date { font-family: var(--font-num); font-size: 12.5px; color: var(--muted); }
/* « En attente · expire le 23/09 à 08:36 » débordait de 3 px à 320 px (iPhone SE de
   première génération, Galaxy Fold replié) et décalait la page latéralement. */
.ns-liste-l .badge { min-width: 0; overflow-wrap: anywhere; white-space: normal; }

/* ---------- Page publique d'ouverture ---------- */
body.ns-page { background: var(--bg); color: var(--fg); }
.ns-shell { max-width: 620px; margin: 0 auto; padding: 24px 16px 40px; }
.ns-card { padding: 20px 18px; }
.ns-marque { margin: 0 0 18px; font-family: var(--font-brand); font-size: 12px; font-weight: 700; letter-spacing: .3em; color: var(--muted); }
.ns-titre { margin: 0 0 14px; font-size: 21px; line-height: 1.25; overflow-wrap: anywhere; }
.ns-avert { margin: 0 0 12px; font-size: 14.5px; line-height: 1.6; }
.ns-avert-fort { border-left: 3px solid var(--warn); padding-left: 12px; }
.ns-delai { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.55; }
.ns-alerte { margin: 0 0 14px; padding: 10px 12px; border-radius: var(--radius-sm); font-size: 13.5px; background: rgba(251, 95, 122, .09); border: 1px solid rgba(251, 95, 122, .4); color: var(--danger); }
.ns-btn { width: 100%; min-height: 48px; margin: 4px 0 0; justify-content: center; }
/* Le secret lui-même. `pre-wrap` : les retours à la ligne et les espaces multiples
   d'un mot de passe sont conservés ; `anywhere` replie une longue clé sans insérer
   de coupure — ce qui est copié est exactement ce qui a été écrit. */
.ns-secret {
  margin: 0 0 12px; padding: 14px;
  font-family: var(--font-num); font-size: 15px; line-height: 1.6;
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: normal;
  background: var(--bg-2); border: 1px solid var(--border-lit); border-radius: var(--radius-sm);
  color: var(--fg); user-select: all; -webkit-user-select: all;
}
.ns-secret:focus { outline: none; box-shadow: var(--glow); }
.ns-pied { margin: 18px 0 0; text-align: center; color: var(--dim); font-size: 11.5px; letter-spacing: .08em; text-transform: uppercase; }

@media (min-width: 640px) {
  .ns-card { padding: 26px 26px 24px; }
  .ns-titre { font-size: 24px; }
}

/* Lien « écran complet » / « voir mes notes » sous un encart : SEUL dans son
   paragraphe, c'est une vraie cible tactile et non un lien en ligne dans une phrase
   — WCAG 2.5.8 demande alors 44 px de haut. Mesuré à 375 px dans Chrome. */
.ns-plus { display: inline-flex; align-items: center; min-height: 44px; }

/* ---------- Impression ----------
   Le destinataire n'a qu'une seule chance : s'il ouvre la note sur un poste qui
   n'est pas celui où il doit saisir le mot de passe, il imprime. Or les navigateurs
   n'impriment PAS les fonds par défaut (« Graphiques d'arrière-plan » décoché) : le
   thème sombre laissait un texte #e9eef8 sur papier blanc, c'est-à-dire une page
   blanche à l'œil nu — et il s'en apercevait une fois la page fermée, donc trop
   tard. Même bloc que public/css/espace.css l. 300 et suivantes. */
@media print {
  body.ns-page { background: #fff !important; color: #000; }
  .ns-card, .ns-lien { background: #fff; border-color: #999; box-shadow: none; }
  .ns-secret, .ns-lien-champ { background: #fff; border-color: #000; color: #000; }
  .ns-marque, .ns-pied, .ns-delai, .ns-aide, .ns-lien-titre { color: #333; }
  .ns-avert, .ns-titre { color: #000; }
}

/* ---------- Saisies sans client (/temps/sans-client) ----------
   Mobile-first : une carte par boutique, une ligne par saisie empilée en
   bloc (jamais de tableau à faire défiler horizontalement sur un téléphone). À
   partir de 640 px, la ligne se remet sur un rang. Mesuré à 375 px. */
.sc-groupe { margin-top: 14px; }
/* Déjà facturées : présentes, mais délibérément SANS alarme. */
.sc-hors { margin-top: 22px; border-style: dashed; opacity: .88; }
.sc-hors .sc-titre { color: var(--muted); }
.sc-tete { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.sc-titre { font-size: 15px; font-weight: 600; color: var(--fg); }
.sc-compte { color: var(--muted); font-size: 12px; font-family: var(--font-num); margin-left: auto; }
.sc-quoi { margin: 10px 0 12px; }
.sc-motif { margin: 0 0 6px; font-size: 13px; line-height: 1.55; color: var(--fg); }
/* Lien d'action SEUL sur sa ligne : vraie cible tactile (WCAG 2.5.8, 44 px). */
.sc-action { display: inline-flex; align-items: center; min-height: 44px; }
.sc-liste { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--border); }
.sc-ligne { padding: 10px 0; border-bottom: 1px solid var(--border); }
.sc-ligne:last-child { border-bottom: 0; padding-bottom: 0; }
.sc-ligne-haut { display: flex; align-items: baseline; gap: 10px; }
.sc-date { font-family: var(--font-num); font-size: 12px; color: var(--muted); }
.sc-duree { font-family: var(--font-num); font-size: 13px; font-weight: 600; margin-left: auto; }
.sc-desc { font-size: 13px; line-height: 1.5; margin-top: 2px; overflow-wrap: anywhere; }
.sc-meta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }

@media (min-width: 640px) {
  .sc-ligne { display: flex; align-items: baseline; gap: 12px; }
  .sc-ligne-haut { flex: 0 0 auto; gap: 12px; }
  .sc-desc { flex: 1; margin-top: 0; }
  .sc-meta { margin-top: 0; }
  .sc-ligne .sc-duree { margin-left: 0; }
}

/* Alerte « n saisies sans client » : un lien, pas un bouton — l'action est d'aller
   voir. Rouge seulement s'il y en a : à zéro, l'indicateur disparaît. */
.sc-alerte {
  display: flex; align-items: center; gap: 8px; min-height: 44px;
  padding: 8px 12px; margin: 0 0 12px;
  border-radius: var(--radius-sm);
  background: rgba(251, 95, 122, .09); border: 1px solid rgba(251, 95, 122, .4);
  color: var(--danger); font-size: 13px; font-weight: 500; text-decoration: none;
}
.sc-alerte:hover { text-decoration: none; border-color: var(--danger); }
.sc-alerte strong { font-family: var(--font-num); }
/* Indicateur du tableau de bord : la même barre que l'état de synchro, mais
   cliquable — l'action attendue est d'aller voir la liste. */

/* ============================================================
   Liaison (lot du 23/09) : hub ↔ cockpits, une ligne par boutique.
   Mobile d'abord : une carte par boutique sous 900 px, grille-tableau au-delà —
   cinq colonnes ne tiennent pas plus tôt sans rogner les motifs d'échec, qui
   sont justement ce qu'on vient lire.
   ============================================================ */
.lsn-entete { margin-bottom: 16px; }
.lsn-tuiles { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.lsn-tuile {
  display: flex; flex-direction: column; gap: 3px; padding: 10px 12px;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.lsn-tuile i { font-style: normal; font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; font-weight: 600; }
.lsn-tuile b { font-size: 20px; line-height: 1.1; }
.lsn-tuile b small { font-size: 12px; color: var(--muted); font-weight: 400; }
.lsn-tuile.ok b { color: var(--success); }
.lsn-tuile.warn b { color: var(--warn); }
.lsn-tuile.bad b { color: var(--danger); }
.lsn-tuile.vide b { color: var(--muted); }
@media (min-width: 640px) { .lsn-tuiles { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.lsn-liste { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.lsn-tete { display: none; }
.lsn-ligne {
  display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr);
  padding: 12px 14px;
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border); border-radius: var(--radius);
}
/* Une boutique dont tout va bien s'efface : l'écran est fait pour les autres. */
.lsn-ligne.lsn-ok { opacity: .82; }
.lsn-ligne.lsn-ok:hover { opacity: 1; }
.lsn-site { display: flex; align-items: center; gap: 9px; min-width: 0; }
.lsn-nom { display: flex; flex-direction: column; min-width: 0; }
/* Le nom peut tenir sur deux lignes : en capitales il déborde la colonne à 375 px,
   et le rogner effacerait ce qui distingue deux boutiques d'un même client. */
.lsn-nom strong { overflow-wrap: anywhere; }
.lsn-nom small { color: var(--muted); font-size: 11.5px; }
.lsn-reveil, .lsn-push, .lsn-livraison { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.lsn-reveil small, .lsn-push small, .lsn-livraison small { font-size: 12px; overflow-wrap: anywhere; }
.lsn-push b, .lsn-livraison b { font-size: 14px; }
.lsn-badges { display: flex; flex-wrap: wrap; gap: 5px; }
.lsn-l { font-style: normal; font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; font-weight: 600; }
.lsn-action { display: flex; }
.lsn-action form { flex: 1; }
/* Cible de 44 px au doigt, pleine largeur sur téléphone : c'est le seul bouton de
   la carte, rien ne sert à le faire chercher. */
.lsn-action .btn { width: 100%; min-height: 44px; justify-content: center; }

.lsn-legende { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; font-size: 12.5px; color: var(--muted); }
.lsn-legende li { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.lsn-legende strong { color: var(--fg); }

@media (min-width: 900px) {
  .lsn-liste { gap: 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
  .lsn-tete, .lsn-ligne {
    display: grid; column-gap: 14px; row-gap: 2px;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1.75fr) minmax(0, .85fr) minmax(0, 1.2fr) 150px;
    align-items: start;
  }
  .lsn-tete {
    padding: 10px 14px; background: rgba(18, 26, 43, .8); border-bottom: 1px solid var(--border-lit);
    color: var(--muted); font-size: 10.5px; text-transform: uppercase; letter-spacing: .12em; font-weight: 700;
  }
  .lsn-ligne { padding: 12px 14px; border: 0; border-top: 1px solid var(--border); border-radius: 0; background: transparent; }
  .lsn-ligne:first-of-type { border-top: 0; }
  .lsn-ligne:hover { background: rgba(53, 208, 242, .05); }
  .lsn-action .btn { width: auto; min-height: 0; }
  /* Libellés des cellules : l'en-tête les porte ; gardés pour les lecteurs d'écran. */
  .lsn-l { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
}

/* ==========================================================================
   CONTEXTE PROJET — le champ unique du header fixe (23/09/2026)
   Un projet choisi, et tout le tableau de bord ne parle plus que de lui.
   Ordinateur : un champ à plat dans le bandeau. Téléphone : une pastille qui
   ouvre le même formulaire dans une boîte sous l'en-tête (le bandeau n'a pas la
   place d'un champ texte à 320/375 px — mesuré avec les chronos).
   Un contexte ACTIF se voit toujours : liseré d'accent, nom en capitales. Un
   filtre global oublié fausse la lecture de tout l'écran.
   ========================================================================== */
.ctx { position: relative; flex: 0 1 230px; min-width: 150px; }
.ctx-bascule { display: none; }
.ctx-form { display: flex; align-items: center; gap: 4px; margin: 0; }
.ctx-combo { flex: 1; min-width: 0; }
.ctx-combo .ctx-ic {
  position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  width: 14px; height: 14px; color: var(--muted); pointer-events: none;
}
.ctx-combo input[type=text] {
  width: 100%; height: 30px; margin: 0; padding: 4px 10px 4px 28px;
  font-size: 12px; color: var(--fg);
  background: var(--bg-2); border: 1px solid var(--border-lit); border-radius: 999px;
}
/* Le nom d'un projet est un nom propre : en capitales (règle de Franck), mais
   pas le texte d'invite. */
.ctx-combo input[type=text]:not(:placeholder-shown) { text-transform: uppercase; font-weight: 600; letter-spacing: .02em; }
.ctx.actif .ctx-combo input[type=text] {
  border-color: var(--accent); background: var(--accent-soft);
  box-shadow: 0 0 12px rgba(53, 208, 242, .18);
}
.ctx.actif .ctx-ic { color: var(--accent); }
/* La liste est plus large que le champ : un « CLIENT — BOUTIQUE » ne tient pas en 200 px. */
.ctx-combo .kl-combo-liste { min-width: 300px; z-index: 70; }
.ctx-raz {
  flex: none; width: 26px; height: 26px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--border-lit); background: var(--bg-2); color: var(--muted);
  font-size: 15px; line-height: 1;
}
.ctx-raz:hover { color: var(--danger); border-color: var(--danger); }
/* Des chronos tournent : le champ cède de la place, comme le reste du bandeau. */
body:has(.chrono-top) .ctx { flex-basis: 180px; min-width: 130px; }

/* Le rappel sous la barre d'outils du tableau de bord. */
.ctx-bandeau {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 14px; padding: 8px 12px; font-size: 13px;
  border: 1px solid rgba(53, 208, 242, .45); border-radius: 8px; background: var(--accent-soft);
}
.ctx-bandeau .ic { width: 16px; height: 16px; flex: none; color: var(--accent); }
.ctx-bandeau form { margin: 0; }
.tk-ctx { margin-left: 8px; font-size: 12px; font-weight: 600; letter-spacing: .03em; color: var(--accent); }

/* Le chiffre « À faire » d'une autre boutique POSE le contexte : un vrai bouton,
   habillé comme le lien qu'il était. */
.parc-vers-projet { display: inline; margin: 0; }
button.chiffre-lien { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; }

/* Jusqu'à 1199 px : PASTILLE. Mesuré entre 900 et 1100 px, le champ à plat
   poussait le « + » du chrono hors de la bande de relevés. */
@media (max-width: 1199px) {
  /* `static` : la boîte se positionne alors sur le bandeau entier (lui-même fixe),
     et jamais hors de l'écran, où que la pastille se trouve dans la barre. */
  .ctx { position: static; flex: none; min-width: 0; }
  .ctx-bascule {
    display: inline-flex; align-items: center; gap: 5px;
    height: 30px; min-width: 32px; padding: 0 8px; cursor: pointer;
    border-radius: 999px; border: 1px solid var(--border-lit); background: var(--bg-2);
    color: var(--muted); font-size: 11px; font-weight: 600; letter-spacing: .02em;
  }
  .ctx-bascule .ic { width: 14px; height: 14px; }
  .ctx-nom { max-width: 72px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Projet posé : la pastille NOMME le projet, et prend la place du logo DELMAK —
     décoratif, le retour à l'accueil est dans les rails (même règle qu'avec un
     chrono). Sans ça, le « + » du chrono partait hors de l'écran à 375 px. */
  /* Sans chrono, le « + » passe EN TÊTE de la bande de relevés : c'est elle qui
     défile quand tout ne tient pas, et le « + » était au bout — hors de l'écran à
     320, 375 et jusqu'à 1100 px (mesuré), donc introuvable sans balayer. Les
     relevés, eux, se relisent sur le tableau de bord. Dès qu'un chrono tourne, il
     reprend sa place APRÈS les chronos (« à la suite »). */
  .tele:not(:has(.chrono-top)) .chrono-neuf { order: -1; }
  /* Chrono en cours : le logo cède sa place (■ recouverts entre 960 et 1180 px). */
  body:has(.chrono-top) .brand { display: none; }
  .ctx:not(.actif) .ctx-nom { display: none; }
  .ctx.actif .ctx-bascule { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
  /* Chrono en cours : la pastille garde sa couleur mais perd le nom — le bouton
     d'arrêt du chrono passe avant (mesuré à 320 px). */
  body:has(.chrono-top) .ctx .ctx-nom { display: none; }
  body:has(.chrono-top) .ctx { flex-basis: auto; min-width: 0; }

  .ctx-form {
    display: none; position: absolute; left: 12px; width: min(420px, calc(100% - 24px)); top: calc(100% + 6px); z-index: 60;
    padding: 10px; gap: 8px;
    background: var(--card); border: 1px solid var(--border-lit); border-radius: 10px;
    box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
  }
  .ctx.ouvert .ctx-form { display: flex; }
  /* 16 px : en dessous, iOS zoome la page au focus. 44 px : cible au doigt. */
  .ctx-combo input[type=text] { height: 44px; font-size: 16px; }
  .ctx-combo .kl-combo-liste { min-width: 0; max-height: 50vh; }
  .ctx-raz { width: 44px; height: 44px; font-size: 20px; }
}
@media (max-width: 899px) {
  /* Projet posé : la pastille NOMME le projet et prend la place du logo DELMAK —
     décoratif, le retour à l'accueil est dans les rails. */
  .hud-top:has(.ctx.actif) .brand { display: none; }
  /* La boîte du « + » s'étale sur le bandeau : où que soit le bouton dans la bande
     (en tête sans chrono), elle reste dans l'écran. */
}

/* 320 px ET trois chronos : il n'y a plus la place de tout (mesuré — les noms sont
   déjà à zéro, le troisième ■ sortait de 5 px). La pastille du contexte s'efface :
   le projet reste dit par le bandeau du tableau de bord, avec son « Tous les sites »,
   alors qu'un bouton d'arrêt hors de l'écran est un chrono qu'on laisse tourner.
   Requête de CONTENEUR et non de média : c'est la largeur du BANDEAU qui compte
   (et c'est elle que mesurent les bancs Chrome, bridés à 320 px dans une fenêtre
   qui ne descend pas sous 500). */
.hud-top { container: hud / inline-size; }
/* 340 px de CONTENU : la requête de conteneur lit la boîte de contenu du bandeau,
   marges intérieures (2 × 12 px) déduites — 320 px d'écran donnent 296 px. */
@container hud (max-width: 340px) {
  .hud-top:has(.chrono-zone .chrono-top:nth-of-type(3)) .ctx { display: none; }
}

/* ---------- Rapports d'audit de la fiche site (AuditReport, 24/09/2026) ----------
   Mobile d'abord : texte au-dessus, actions en dessous qui passent à la ligne. */
.ar-liste { list-style: none; margin: 0 0 6px; padding: 0; }
.ar-ligne { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 10px; padding: 10px 0; border-top: 1px solid var(--border); }
.ar-ligne:first-child { border-top: 0; }
.ar-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 260px; }
.ar-txt > span { overflow-wrap: anywhere; }
.ar-etats { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 2px; }
.ar-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.ar-actions form { margin: 0; }
.ar-actions .btn { min-height: 36px; }
.ar-depot .ar-case { display: flex; align-items: flex-start; gap: 8px; margin: 6px 0; font-weight: 400; }
.ar-depot .ar-case input { margin-top: 3px; flex: none; }
.ar-depot .btn { margin-top: 6px; min-height: 40px; }

/* ---------- Tâches cron (admin + espace) ----------
   Webcrons des sites (26/09/2026) : carte de la fiche, écran de la tâche, formulaire,
   détail d'une exécution. Feuille chargée AUSSI par l'espace client : classes .wc-*
   seulement. Mobile d'abord, aucune largeur fixe ; URL, en-têtes et réponses se replient
   (overflow-wrap: anywhere) : rien ne déborde à 375 px. */
.wc-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-bottom: 10px; }
.wc-tete h2 { margin: 0; }
.wc-titre { display: flex; flex-wrap: wrap; align-items: center; gap: 5px 8px; }
.wc-titre strong, .wc-url, .wc-message { overflow-wrap: anywhere; }
/* Titre de l'écran de la tâche : .dsp-fiche-titre h1 est en flex (le texte devient un élément
   anonyme qui ne descend pas sous son plus long mot) ; un libellé snake_case déborderait. */
.dsp-fiche-titre .wc-h1 { min-width: 0; overflow-wrap: anywhere; }
.wc-etat-ligne { gap: 6px 8px; }
.wc-etat-ligne .small { overflow-wrap: anywhere; }
.wc-etat dd, .wc-detail dd { overflow-wrap: anywhere; }
.wc-attente { margin: 12px 0 0; padding: 10px 12px; border: 1px solid var(--border-lit); border-left: 3px solid var(--muted); border-radius: var(--radius-sm); background: var(--bg-2); font-size: 13px; }
.wc-attente.recente { border-left-color: var(--accent); }
.wc-attente a { display: inline-block; margin-left: 6px; padding: 4px 0; white-space: nowrap; }
/* Formulaire : un bloc par type de fréquence, affiché en CSS (aucun script : la CSP de
   l'espace bloque le script en ligne). Sans :has(), tous les blocs restent visibles. */
.wc-freq { min-width: 0; margin: 0 0 14px; padding: 0; border: 0; }
.wc-freq legend, .wc-sous-titre { display: block; padding: 0; margin: 0 0 5px; font-size: 10.5px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); }
.wc-types { display: grid; grid-template-columns: 1fr; gap: 6px; margin-bottom: 12px; }
.wc-type { align-items: center; min-height: 44px; margin: 0; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-2); }
.wc-type input { margin-top: 0; }
.wc-type:has(input:checked) { border-color: var(--accent); }
.wc-f { display: none; }
form:has(#wc-t-minutes:checked) .wc-f-minutes,
form:has(#wc-t-heures:checked) .wc-f-heures,
form:has(#wc-t-semaine:checked) .wc-f-semaine,
form:has(#wc-t-mois:checked) .wc-f-mois,
form:has(#wc-t-jour:checked) .wc-f-heure,
form:has(#wc-t-semaine:checked) .wc-f-heure,
form:has(#wc-t-mois:checked) .wc-f-heure { display: block; }
@supports not selector(:has(*)) { .wc-f { display: block; } }
.wc-jours { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.wc-jours .dsp-case { align-items: center; min-height: 44px; margin: 0; padding: 0 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-2); }
.wc-jours .dsp-case input { margin-top: 0; }
.wc-f input[type=time] {
  width: 100%; max-width: 180px; background: var(--bg-2); color: var(--fg); color-scheme: dark;
  border: 1px solid var(--border); border-radius: 10px; padding: 10px 13px; font: 400 13.5px var(--font); line-height: 1.45; outline: none;
}
.wc-form input.invalide, .wc-form select.invalide { border-color: var(--danger); }
.wc-types[aria-invalid="true"] .wc-type, .wc-jours[aria-invalid="true"] .dsp-case { border-color: rgba(251, 95, 122, .6); }
.wc-boutons { margin: 4px 0 0; }
.wc-apercu { border-color: rgba(53, 208, 242, .45); }
.wc-date { white-space: nowrap; }
/* Historique : une ligne = un lien de 44 px au moins, sur deux rangées sous 640 px. */
.wc-hist-tete { gap: 8px 10px; }
/* Onglets « Toutes N · Réussies N · Échecs N » : plus larges qu'un écran de 320 px avec leurs
   compteurs, ils passent à la ligne (même règle que la .segmented de l'espace). */
.wc-hist-tete .dsp-onglets { flex-wrap: wrap; max-width: 100%; }
.wc-n { margin-left: 2px; font-family: var(--font-num); opacity: .75; }
.wc-hist { list-style: none; margin: 0; padding: 0; }
.wc-hist li + li { border-top: 1px solid var(--border); }
.wc-hist a { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; min-height: 44px; padding: 8px 26px 8px 4px; border-radius: var(--radius-sm); color: var(--fg); text-decoration: none; }
.wc-hist a:hover, .wc-hist a:focus-visible { background: rgba(255, 255, 255, .04); }
.wc-h-l1, .wc-h-l2 { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; min-width: 0; flex: 1 1 100%; }
.wc-h-l2 { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.wc-h-date { font-family: var(--font-num); font-size: 12.5px; }
.wc-h-cause { color: var(--danger); overflow-wrap: anywhere; }
.wc-h-chevron { position: absolute; top: 50%; right: 8px; transform: translateY(-50%); color: var(--muted); font-size: 16px; }
@media (min-width: 640px) {
  .wc-types { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
  .wc-jours { grid-template-columns: repeat(7, minmax(0, 1fr)); }
  .wc-h-l1 { flex: 0 0 auto; min-width: 15.5em; }
  .wc-h-l2 { flex: 1 1 0; }
}
/* Réponse d'une boutique : échappée, repliée, jamais plus large que l'écran. */
.wc-pre { margin: 0; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; font: 12px/1.5 ui-monospace, monospace; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 60vh; overflow: auto; }

/* ============ Dashboard : mode « Disposer » ============ */
/* Déplacer les encarts et choisir leur largeur (demande de Franck du 24/09/2026).
   `.btn` impose display:inline-flex, qui annulerait l'attribut hidden : garde-fous. */
.dsp-bouton[hidden], .dsp-barre[hidden], .dsp-barre .btn[hidden], .dsp-note[hidden] { display: none; }
.dsp-barre {
  position: sticky; top: 54px; z-index: 30;
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  margin: 0 0 14px; padding: 10px 12px;
  background: var(--card); border: 1px solid var(--border-lit); border-radius: var(--radius); box-shadow: var(--lift);
}
@media (max-width: 899px) { .dsp-barre { top: 48px; } }          /* mêmes valeurs que .tk-statuts */
.dsp-titre { font-family: var(--font-brand); font-size: 13px; text-transform: uppercase; letter-spacing: .13em; outline: none; }
.dsp-etat { color: var(--muted); }
.dsp-aide { flex-basis: 100%; margin: 0; }
/* Repère absolu ; tuiles d'une même rangée à la même hauteur. */
.dash.dsp-on { position: relative; align-items: stretch; }
/* Vue schéma : le contenu disparaît (ni lien, ni ligne cliquable, ni piste météo, ni
   poignée de ticket sous le doigt). !important : des enfants portent des display plus
   spécifiques ou en ligne. */
.dash.dsp-on > .card > :not(.dsp-outils) { display: none !important; }
.dash.dsp-on > .card { padding: 10px 12px; border-style: dashed; border-color: var(--border-lit); }
.dash.dsp-on > .card::after { display: none; }
@media (min-width: 900px) {
  /* 6 bandes de 4 pistes derrière les tuiles : les trous se voient. */
  .dash.dsp-on {
    background-image: linear-gradient(to right, rgba(53, 208, 242, .06) calc(100% - 14px), transparent 0);
    background-size: calc((100% + 14px) / 6) 100%;
  }
}
.dsp-outils { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-height: 44px; }
.dsp-nom { flex: 1 1 7em; min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
.dsp-note { flex-basis: 100%; margin: 0; font-size: 12px; color: var(--muted); }
.dsp-outils button { touch-action: manipulation; }
.dsp-poignee { touch-action: none; cursor: grab; font-size: 15px; }   /* condition du glisser au doigt */
@media (pointer: coarse) { .dsp-outils button { min-width: 44px; min-height: 44px; } }
.dsp-larg { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.dsp-larg-titre { font-size: 12px; color: var(--muted); }
@media (min-width: 900px) { .dsp-larg-titre { display: none; } }
.dsp-larg button[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.dsp-outils [aria-disabled="true"] { opacity: .35; cursor: default; }
.dsp-bord {
  position: absolute; top: 50%; right: -7px; width: 14px; height: 36px; margin-top: -18px;
  border-radius: 7px; background: var(--border-lit); cursor: ew-resize; touch-action: none;
}
.dsp-bord:hover { background: var(--accent); }
@media (max-width: 899px), (pointer: coarse) { .dsp-bord { display: none; } }
.dsp-fantome { opacity: .7; }
.dash.dsp-on > .dsp-glisse { z-index: 20; transition: none; cursor: grabbing; box-shadow: var(--lift); }
body.dsp-glisse-en-cours { user-select: none; -webkit-user-select: none; cursor: grabbing; }
.dsp-repere { position: absolute; z-index: 21; background: var(--accent); border-radius: 2px; pointer-events: none; box-shadow: 0 0 10px rgba(53, 208, 242, .6); }
@media (prefers-reduced-motion: reduce) { .dash.dsp-on * { transition: none !important; } }

/* ============ Facture libre (/facturation/nouvelle, 25/09/2026) ============ */
.fl-carte { max-width: 760px; }
.fl-titre { margin: 18px 0 8px; font-size: 14px; }
.fl-erreurs { margin: 0; padding-left: 18px; }
.fl-lignes { display: grid; gap: 10px; }
/* Téléphone : libellé sur toute la largeur, montant et ✕ dessous. */
.fl-ligne { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px 8px; align-items: end; }
.fl-ligne .field { margin-bottom: 0; min-width: 0; }
.fl-libelle { grid-column: 1 / -1; }
.fl-montant input { text-align: right; font-variant-numeric: tabular-nums; }
.fl-suppr { min-height: 40px; }
@media (min-width: 640px) {
  .fl-ligne { grid-template-columns: minmax(0, 1fr) 160px auto; }
  .fl-libelle { grid-column: auto; }
  /* Une seule ligne d'intitulés : les suivantes gardent leurs libellés pour les lecteurs d'écran. */
  .fl-ligne + .fl-ligne label {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
}
.fl-ajouter { margin-top: 10px; }
.fl-totaux {
  display: grid; grid-template-columns: auto auto; justify-content: end; gap: 4px 18px;
  margin: 16px 0 0; font-variant-numeric: tabular-nums;
}
.fl-totaux dt { color: var(--muted); text-align: right; }
.fl-totaux dd { margin: 0; text-align: right; font-family: var(--font-num); }
.fl-totaux .fl-ttc { font-weight: 700; color: var(--fg); }
.fl-actions { margin-top: 16px; display: flex; justify-content: flex-end; }
@media (max-width: 639px) { .fl-actions .btn { width: 100%; } }
.fl-cache {
  position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.fl-montant input[aria-invalid="true"] { border-color: var(--danger); box-shadow: 0 0 0 1px var(--danger); }
.fl-boutique[hidden], .fl-plein[hidden] { display: none; }

/* ---------- Abonnements mensuels (fiche site, 30/09/2026) ---------- */
.abo-liste { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 8px; }
.abo { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 9px 11px; display: grid; gap: 3px; }
.abo-arrete { opacity: .6; }
.abo-tete { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.abo-montant { font-family: var(--font-num); font-size: 13px; white-space: nowrap; }
.abo-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.abo-actions form { margin: 0; }
.abo-form { display: grid; gap: 8px; grid-template-columns: 1fr; align-items: end; }
@media (min-width: 640px) { .abo-form { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto; } }
.abo-form .field { margin: 0; }
.abo-envoi .btn { width: 100%; }
@media (max-width: 767px) { .abo-actions .btn, .abo-envoi .btn { min-height: 44px; } }

/* ---------- Infobulle des lignes de facturation (01/10/2026) ---------- */
.fac-tip {
  position: fixed; z-index: 140; pointer-events: none;
  width: max-content; max-width: min(460px, calc(100vw - 16px));
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: linear-gradient(180deg, #172238, #0e1626); border: 1px solid var(--border-lit);
  box-shadow: var(--lift), var(--glow); font-size: 12.5px; color: var(--fg);
}
.fac-tip-tete { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.fac-tip-tete strong { color: var(--fg); font-family: var(--font-num); }
.fac-tip-liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px; }
.fac-tip-liste li { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; gap: 8px; align-items: baseline; }
.fac-tip-date, .fac-tip-duree { font-family: var(--font-num); font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.fac-tip-duree { color: var(--accent); text-align: right; }
.fac-tip-lib { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fac-tip-tk { font-family: var(--font-num); font-size: 11px; color: var(--accent-2); }
.fac-tip-site { font-size: 10.5px; color: var(--dim); }
.fac-tip-plus, .fac-tip-vide { margin-top: 6px; font-size: 11.5px; color: var(--muted); }

/* Visionneuse d'images (01/10/2026) : <dialog> plein écran, image entière, mobile d'abord. */
.visio {
  position: fixed; inset: 0; width: 100vw; height: 100dvh; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: rgba(3, 6, 12, .92); color: #e8eef7;
  display: none; overscroll-behavior: contain;
}
.visio[open] { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; grid-template-rows: auto minmax(0, 1fr); }
.visio::backdrop { background: rgba(3, 6, 12, .6); backdrop-filter: blur(4px); }
.visio-barre {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 10px;
  padding: max(8px, env(safe-area-inset-top)) 12px 8px 16px; min-width: 0;
}
.visio-legende { flex: 1 1 auto; min-width: 0; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.visio-compte { font-family: var(--font-num); font-size: 12px; color: #9fb0c8; white-space: nowrap; }
.visio-bt {
  display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px;
  border-radius: 8px; border: 1px solid rgba(255, 255, 255, .14); background: rgba(255, 255, 255, .06);
  color: #e8eef7; font-size: 22px; line-height: 1; text-decoration: none; cursor: pointer;
}
.visio-bt:hover, .visio-bt:focus-visible { background: rgba(53, 208, 242, .18); text-decoration: none; }
.visio-cadre { grid-row: 2; grid-column: 2; margin: 0; display: flex; align-items: center; justify-content: center; min-height: 0; padding: 0 0 max(12px, env(safe-area-inset-bottom)); }
.visio-img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 4px; box-shadow: 0 10px 40px rgba(0, 0, 0, .5); background: #fff1; }
.visio-nav {
  grid-row: 2; align-self: center; width: 44px; height: 72px; margin: 0 6px;
  border: 0; border-radius: 8px; background: rgba(255, 255, 255, .06); color: #e8eef7; font-size: 34px; cursor: pointer;
}
.visio-nav:hover, .visio-nav:focus-visible { background: rgba(53, 208, 242, .18); }
.visio-nav[hidden] { display: block; visibility: hidden; }
.visio-prec { grid-column: 1; }
.visio-suiv { grid-column: 3; }
@media (max-width: 599px) { .visio-nav { width: 36px; margin: 0 2px; } }
.ticket-desc img { cursor: zoom-in; }
/* Visionneuse de documents (09/10/2026) : PDF et aperçus d'e-mail en cadre, sous la barre. */
.visio-doc[open] { display: flex; flex-direction: column; }
.visio-bt[hidden], .visio-doc-attente[hidden] { display: none; }
.visio-doc-corps { position: relative; flex: 1 1 auto; min-height: 0; padding: 0 12px max(12px, env(safe-area-inset-bottom)); overflow: auto; -webkit-overflow-scrolling: touch; }
.visio-doc-cadre { display: block; width: 100%; height: 100%; border: 0; border-radius: 6px; background: #fff; box-shadow: 0 10px 40px rgba(0, 0, 0, .5); }
.visio-doc-html .visio-doc-cadre { max-width: 860px; margin: 0 auto; }
.visio-doc-attente { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; margin: 0; color: #9fb0c8; font-size: 13px; pointer-events: none; }
.visio-doc-refus { display: grid; justify-items: center; gap: 12px; padding: 48px 16px; text-align: center; }
@media (max-width: 599px) { .visio-doc-corps { padding: 0 0 env(safe-area-inset-bottom); } .visio-doc-cadre { border-radius: 0; } }

/* Relevés de l'en-tête rechargés en direct (01/10/2026) : le conteneur ne crée pas de boîte. */
.tele-direct { display: contents; }
.tele-direct > * { flex: none; }
/* Encart mis à jour en direct : un bref liseré, pour qu'on voie ce qui a bougé. */
@keyframes kl-direct-maj { 0% { box-shadow: 0 0 0 1px rgba(53, 208, 242, .7), 0 0 22px rgba(53, 208, 242, .35); } 100% { box-shadow: var(--lift, none); } }
.kl-direct-maj { animation: kl-direct-maj 1.5s ease-out; }
@media (prefers-reduced-motion: reduce) { .kl-direct-maj { animation: none; } }

/* ── Modale Stats (01/10/2026) ─────────────────────────────────────────────────────
   Mobile d'abord : plein écran sous 900 px, réglages empilés, tuiles en colonne. */
@media (max-width: 899px) { .top-stats { display: inline-flex; } }
.stats-dlg {
  width: min(1100px, calc(100vw - 48px));
  max-height: calc(100dvh - 48px);
  max-width: none; margin: auto; padding: 0; color: var(--fg);
  background: linear-gradient(180deg, var(--card-2), var(--card));
  border: 1px solid var(--border-lit); border-radius: 14px;
  box-shadow: 0 40px 110px rgba(0, 0, 0, .78), 0 0 0 1px rgba(53, 208, 242, .14);
  overflow: hidden auto; overscroll-behavior: contain;
}
.stats-dlg[open] { display: flex; flex-direction: column; }
.stats-dlg::backdrop { background: rgba(4, 7, 13, .74); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.stats-tete { display: flex; align-items: center; gap: 12px; padding: 14px 16px 6px 20px; position: sticky; top: 0; background: inherit; z-index: 1; }
.stats-titre { margin: 0; font-size: 18px; }
.stats-portee { font-size: 12px; color: var(--muted); letter-spacing: .04em; }
.stats-fermer { width: 44px; height: 44px; border: 0; border-radius: 8px; background: none; color: var(--muted); font-size: 24px; cursor: pointer; }
.stats-fermer:hover, .stats-fermer:focus-visible { color: var(--fg); background: var(--accent-soft); }
.stats-reglages { display: flex; flex-wrap: wrap; gap: 8px 24px; padding: 4px 20px 10px; border-bottom: 1px solid var(--border); }
.stats-ligne { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px; }
.stats-dates, .stats-comparaison { display: inline-flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px; }
.stats-dates[hidden], .stats-comparaison[hidden] { display: none; }
.stats-champ { display: grid; gap: 4px; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.stats-champ select, .stats-champ input { min-height: 38px; font-size: 14px; text-transform: none; letter-spacing: 0; }
.stats-coche { display: inline-flex; align-items: center; gap: 8px; min-height: 38px; font-size: 13px; color: var(--fg); cursor: pointer; }
.stats-corps { padding: 12px 20px 18px; }
.stats-corps[aria-busy="true"] .stats-chiffres, .stats-corps[aria-busy="true"] .stats-graphe { opacity: .55; transition: opacity .2s; }
.stats-libelles { margin: 0 0 10px; font-size: 13px; color: var(--muted); }
.stats-chiffres { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 640px) { .stats-chiffres { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.stats-tuile { display: grid; gap: 4px; padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px; background: rgba(255, 255, 255, .02); min-width: 0; }
.stats-tuile-titre { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.stats-tuile-valeur { font-family: var(--font-num); font-size: 26px; font-weight: 700; color: var(--accent); line-height: 1.1; }
.stats-tuile-detail { font-size: 12px; color: var(--dim); overflow-wrap: anywhere; }
.stats-comp { color: var(--muted); }
.stats-delta.hausse { color: #38d39f; }
.stats-delta.baisse { color: #ff6b6b; }
.stats-graphe { position: relative; height: 320px; margin-top: 14px; }
.stats-note { margin: 10px 0 0; }
@media (max-width: 899px) {
  .stats-dlg { width: 100vw; max-height: 100dvh; height: 100dvh; border: 0; border-radius: 0; }
  .stats-graphe { height: 260px; }
  .stats-tuile-valeur { font-size: 22px; }
}

/* ---------- Volet « Tâches cron » (08/10/2026) ---------- */
.wcv-nouvelle { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.wcv-nouvelle select { max-width: 260px; min-height: 36px; }
.wcv-filtres { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 10px 12px; margin: 0 0 12px; }
.wcv-filtres input[type="search"] { flex: 1 1 200px; min-width: 0; min-height: 36px; }
.wcv-filtres select { min-height: 36px; max-width: 100%; }
.wcv-groupe .wc-tete h2 { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.wcv-compte { margin: 0 0 8px; }
.wcv-nouvelle .btn, .wcv-filtres .btn { min-height: 36px; }
@media (max-width: 760px) {
  .wcv-nouvelle .btn, .wcv-filtres .btn, .wcv-filtres select, .wcv-filtres input[type="search"], .wcv-nouvelle select { min-height: 40px; }
  .wcv-nouvelle, .wcv-nouvelle select { width: 100%; max-width: none; }
  .wcv-filtres select, .wcv-filtres .btn { flex: 1 1 100%; }
}

/* CHEMINS DE FICHIERS (filtre Twig « chemin », 19/10/2026) : un caractère invisible ou de direction dans un chemin
   reçu d'une boutique est montré en étiquette (« U+202E »), jamais appliqué. */
.chemin-invisible {
  display: inline-block; margin: 0 1px; padding: 0 3px; border-radius: 3px;
  font: 600 10px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; vertical-align: 1px;
  color: var(--danger, #ff6b6b); background: rgba(255, 107, 107, .14); border: 1px solid rgba(255, 107, 107, .45);
}

/* ============================================================
   TABLETTE AU DOIGT (iPad Pro 13, 31/10/2026)
   Franck : « utiliser DELMAK à toutes ses capacités sur l'iPad, la même version que sur
   le MacBook ». Même mise en page que l'ordinateur ; seules les CIBLES grandissent quand
   le pointeur principal est le doigt. Le Mac (pointeur fin) ne lit aucune de ces règles.
   ============================================================ */
@media (pointer: coarse) {
  /* En-tête (46 px de haut, inchangé) : des boutons de 38-40 px au lieu de 25-30. */
  .chrono-stop { width: 38px; height: 38px; font-size: 11px; }
  .top-new, .pal-open, .top-memo { min-height: 38px; padding-inline: 11px; }
  .top-son { width: 40px; height: 38px; }
  a.tele-item { min-height: 38px; display: inline-flex; align-items: center; }
  /* Actions des cartes ticket, aussi dans la liste /tickets (l'encart a sa règle dans tickets.css). */
  .tk-act { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; font-size: 15px; padding: 0 10px; }
  .tk-actions { gap: 8px; }
  /* Petits boutons communs : Facturation (Télécharger / Envoi test / Envoyer), Temps, encart Claude. */
  .btn.btn-sm { min-height: 40px; padding-inline: 14px; }
  /* Fenêtres : ✕ fermer et ‹ retour. */
  .mdl-nav { width: 44px; height: 44px; }
  /* Listes de choix (projet du chrono, sélecteur de l'en-tête) et palette « Aller à… ».
     Padding plutôt que flex : l'ellipse des noms longs reste. */
  .kl-combo-liste li { padding-top: 12px; padding-bottom: 12px; }
  .pal-item { min-height: 44px; }
}
/* Sans clavier physique, les rappels ⌘K / ⌘+↵ ne servent à rien. */
@media (hover: none) and (pointer: coarse) {
  kbd, .tq-astuce { display: none; }
}
