:root {
  /* Palette tirée des deux températures de la lumière de projection : le
     tungstène chaud d'une cabine et la lumière du jour. Le fond est un noir
     chaud, encre plutôt qu'ardoise ; l'accent tire vers l'indigo, et l'ambre
     signale ce qui demande un regard. Le gris-bleu froid d'avant faisait
     ressembler un outil de laboratoire cinéma à une console d'administration. */
  --bg: #14133a; --surface: rgba(30, 29, 74, .74); --raised: #262555;
  --line: rgba(255, 255, 255, .09);
  --text: #eeecfb; --muted: #a5a1c8; --accent: #7d7bf5; --warm: #e8a34a;
  --error: #ff8a8a; --ok: #4ad4a0;

  /* Le vert système d'Apple, variante sombre. Demandé par l'exploitant, et il
     tombe juste : ce vert-là est réglé pour les fonds sombres, là où notre
     « --ok » turquoise vire au pâle. Il ne sert qu'à la progression d'un
     fichier, où il veut dire « ça avance, et ça se passe bien ». */
  --vert-systeme: #30d158;

  /* Matières. La barre latérale est structurelle, donc plus dense et plus
     sombre que le contenu ; les surfaces de contenu restent opaques pour ne
     jamais empiler deux translucidités l'une sur l'autre. */
  --chrome: rgba(21, 20, 56, .62);
  --chrome-edge: rgba(255, 255, 255, .06);
  --scrim: rgba(9, 10, 13, .55);

  /* Élévation : plus une surface est large, plus son ombre est profonde. */
  --lift-1: 0 1px 2px rgba(12,8,20,.34);
  --lift-2: 0 2px 6px rgba(8,6,30,.40), 0 10px 30px rgba(8,6,30,.30);
  --lift-3: 0 6px 18px rgba(8,6,30,.46), 0 28px 64px rgba(8,6,30,.38);

  /* Une seule durée de référence. Tout ce qui répond à un doigt ou à un
     curseur reste sous 120 ms — au-delà, le geste et l'effet se décollent. */
  --tempo: 140ms;
  --tempo-press: 90ms;
  --courbe: cubic-bezier(.22, .61, .36, 1);

  --rail: 236px;

  /* Voiles posés sur le fond. Sortis en variables parce qu'ils s'inversent
     d'un thème clair à un thème sombre : une tuile éclaircie sur du papier
     blanc ne se voit pas. */
  --tuile: rgba(255, 255, 255, .04);
  --tuile-survol: rgba(255, 255, 255, .08);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0; background: var(--bg); color: var(--text);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Corps en rem : la taille de texte du système doit pouvoir agrandir
     l'interface sans casser la mise en page. */
  font-size: 0.9375rem; line-height: 1.55;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Un anneau de focus visible au clavier, invisible à la souris. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 6px;
}

.skip {
  position: absolute; left: -9999px; top: 8px; z-index: 100;
  padding: 10px 16px; border-radius: 8px; background: var(--accent); color: #fff;
}
.skip:focus { left: 8px; }

/* --- Colonne de navigation ----------------------------------------------
   Barre latérale plutôt que bandeau : à dix-sept destinations, un rang
   horizontal ne répond plus à « où suis-je et où puis-je aller ». Les entrées
   sont groupées par moment du métier, dans l'ordre où il se déroule. */
.sidebar {
  position: fixed; inset: 0 auto 0 0; width: var(--rail); z-index: 20;
  display: flex; flex-direction: column;
  padding: 18px 12px 12px;
  background: var(--chrome);
  backdrop-filter: blur(24px) saturate(150%);
  -webkit-backdrop-filter: blur(24px) saturate(150%);
  border-right: 1px solid var(--line);
  overflow-y: auto; overscroll-behavior: contain;
}

.nav { display: flex; flex-direction: column; gap: 1px; flex: 1; }
.nav-group {
  margin: 18px 10px 6px; font-size: 0.6875rem; font-weight: 600;
  /* Du thème, et non d'une constante : ce mauve clair disparaissait sur le
     thème papier, où la barre devient blanche. */
  text-transform: uppercase; letter-spacing: .06em; color: var(--muted); opacity: .85;
}
.nav-group:first-child { margin-top: 0; }
.nav a {
  padding: 7px 10px; border-radius: 7px; color: var(--muted);
  font-size: 0.875rem; line-height: 1.35;
  transition: background-color var(--tempo) var(--courbe), color var(--tempo) var(--courbe);
}
/* Un lavis blanc sur une barre blanche ne se voit pas : on prend la teinte
   de survol du thème, qui s'assombrit quand le fond s'éclaircit. */
.nav a:hover { color: var(--text); background: var(--tuile); text-decoration: none; }
.nav a:active { background: var(--tuile-survol); }
.nav a[aria-current="page"] {
  color: var(--text); background: rgba(125,123,245,.14);
  box-shadow: inset 0 0 0 1px rgba(125,123,245,.22);
}

.sidebar-foot {
  margin-top: 16px; padding: 12px 10px 4px; border-top: 1px solid var(--line);
  display: grid; gap: 3px;
}
.who { color: var(--text); font-size: 0.8125rem; overflow-wrap: anywhere; }
.role {
  justify-self: start; padding: 1px 8px; border: 1px solid var(--line);
  border-radius: 999px; font-size: 0.625rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted);
}
.quit { margin-top: 6px; font-size: 0.8125rem; color: var(--muted); }
.quit:hover { color: var(--text); }

/* --- Navigation sur écran étroit -----------------------------------------
   Deux régimes, et non un seul dégradé. Sur tablette la colonne se couche en
   rangée : dix-sept destinations tiennent encore sur deux lignes. Sur
   téléphone elles n'y tiennent plus — la rangée devient une pile qui repousse
   le contenu hors de l'écran, et l'on ouvre l'outil sur un menu au lieu de
   son travail. Sous 760 px la colonne devient donc un tiroir. */

.barre-mobile { display: none; }
.voile { display: none; }

@media (min-width: 761px) and (max-width: 900px) {
  .sidebar {
    position: static; width: auto; flex-direction: row; flex-wrap: wrap;
    align-items: center; gap: 4px 10px; border-right: 0;
    border-bottom: 1px solid var(--line); padding: 12px 16px;
  }
  .brand { margin: 0 12px 0 0; }
  .nav { flex-direction: row; flex-wrap: wrap; gap: 2px 4px; }
  .nav-group { display: none; }
  .sidebar-foot {
    margin: 0 0 0 auto; padding: 0; border-top: 0;
    grid-auto-flow: column; align-items: center; gap: 10px;
  }
}

@media (max-width: 760px) {
  .barre-mobile {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 10px 14px;
    position: sticky; top: 0; z-index: 30;
    background: var(--chrome);
    backdrop-filter: blur(24px) saturate(150%);
    -webkit-backdrop-filter: blur(24px) saturate(150%);
    border-bottom: 1px solid var(--line);
  }
  .brand-mini { display: block; line-height: 0; }
  /* Le logo est un dessin noir : sur fond sombre il faut l'inverser, comme
     ailleurs. Sans cela il disparaît dans la barre. */
  .brand-mini img { width: 108px; height: auto; filter: invert(1); opacity: .92; }

  .menu-bouton {
    /* 44 px : la cible tactile la plus petite qu'un pouce atteigne sans viser. */
    width: 44px; height: 44px; flex: none;
    display: grid; place-items: center;
    background: transparent; border: 1px solid var(--line); border-radius: 10px;
    color: var(--text); cursor: pointer;
    transition: background-color var(--tempo) var(--courbe);
  }
  .menu-bouton:hover { background: rgba(255,255,255,.05); }
  .menu-bouton:active { background: rgba(255,255,255,.09); }
  .menu-bouton .ic { width: 22px; height: 22px; opacity: 1; }

  .sidebar {
    position: fixed; inset: 0 auto 0 0; width: min(84vw, 300px);
    z-index: 40; border-right: 1px solid var(--line);
    transform: translateX(-100%);
    transition: transform 220ms var(--courbe);
    padding: 18px 12px 24px;
  }
  .sidebar.ouvert { transform: translateX(0); box-shadow: 0 0 60px rgba(0,0,0,.5); }

  .voile {
    display: block; position: fixed; inset: 0; z-index: 35;
    background: rgba(8, 7, 26, .6);
    backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  }

  /* Le corps ne défile plus derrière le tiroir : sans cela, un geste destiné
     au menu emporte la page et l'on perd sa place. */
  body.fige { overflow: hidden; }
}

.page { max-width: 1100px; margin: 0 auto; padding: 34px 32px 72px; }

/* La colonne est fixe : c'est au contenu de lui réserver sa place, par une
   marge intérieure et non par une marge extérieure — une marge extérieure
   serait écrasée par le centrage automatique. La largeur maximale absorbe le
   rail pour que la colonne de lecture garde sa mesure. */
@media (min-width: 901px) {
  .sidebar ~ .page {
    padding-left: calc(var(--rail) + 32px);
    max-width: calc(1100px + var(--rail) + 32px);
  }
}
@media (max-width: 900px) { .page { padding: 24px 20px 56px; } }
@media (max-width: 760px) { .page { padding: 20px 16px 56px; } }

.auth { display: grid; place-items: center; min-height: 100vh; padding: 24px; }

/* --- Type ---------------------------------------------------------------
   L'interlettrage suit la taille : un titre grandit et ses lettres semblent
   s'écarter, il faut donc resserrer ; un petit texte a besoin de l'inverse.
   Une valeur unique pour toutes les tailles est fausse quelque part. */
h1 {
  font-size: 1.5rem; line-height: 1.2; letter-spacing: -0.02em;
  margin: 0 0 4px; font-weight: 600;
}
h2 { font-size: 1.0625rem; line-height: 1.3; letter-spacing: -0.01em; font-weight: 600; }
.sub { color: var(--muted); margin: 0 0 24px; font-size: 0.875rem; }

.card {
  width: 100%; max-width: 360px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 12px; padding: 28px;
}
.card.wide { max-width: 440px; }
.card label { display: block; margin: 16px 0 6px; font-size: 13px; color: var(--muted); }
.card input {
  width: 100%; padding: 10px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text); font-size: 15px;
}
.card input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.card button {
  width: 100%; margin-top: 22px; padding: 11px; border: 0; border-radius: 8px;
  background: var(--accent); color: #fff; font-size: 15px; font-weight: 500; cursor: pointer;
}
.card button:hover { filter: brightness(1.08); }
.card .secondary { display: block; text-align: center; margin-top: 14px; font-size: 13px; }

.qr { display: block; width: 200px; height: 200px; margin: 8px auto 16px; border-radius: 8px; background: #fff; padding: 6px; }
.secret { display: block; margin-top: 8px; padding: 8px; background: var(--bg);
          border-radius: 6px; font-size: 11px; word-break: break-all; color: var(--muted); }
details summary { cursor: pointer; color: var(--muted); font-size: 13px; }

.error, .field-error {
  margin-top: 8px; padding: 9px 11px; border-radius: 8px; font-size: 13px;
  background: rgba(255,122,122,.1); color: var(--error); border: 1px solid rgba(255,122,122,.25);
}
.messages { list-style: none; padding: 0; margin: 0 0 20px; }
.msg { padding: 10px 14px; border-radius: 8px; margin-bottom: 8px; font-size: 14px;
       background: var(--surface); border: 1px solid var(--line); }
.msg.success { border-color: rgba(74,212,160,.35); color: var(--ok); }
.msg.error { border-color: rgba(255,122,122,.35); color: var(--error); }

.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
.tile {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; padding: 20px; box-shadow: var(--lift-1);
}
.tile h2 { font-size: 13px; color: var(--muted); margin: 0 0 10px; font-weight: 500;
           text-transform: uppercase; letter-spacing: .4px; }
.metric {
  font-size: 1.75rem; line-height: 1.1; letter-spacing: -0.02em;
  margin: 0; font-variant-numeric: tabular-nums;
}

/* --- En-tête de page ----------------------------------------------------- */
.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 24px; margin-bottom: 28px; flex-wrap: wrap;
}
.page-head h1 { margin-bottom: 6px; }
.page-head .sub { margin: 0; }
.crumb { margin: 0 0 8px; font-size: 13px; }
.crumb a { color: var(--muted); }
.actions { display: flex; gap: 10px; align-items: center; }
.actions form { margin: 0; }

/* --- Boutons ------------------------------------------------------------- */
/* « hidden » doit gagner, toujours.
 *
 * C'est un « display: none » d'agent utilisateur, donc de spécificité nulle :
 * le moindre « display: flex » ou « display: grid » de classe l'écrase, et
 * l'élément reste à l'écran alors que le code le croit caché. Le piège s'est
 * refermé cinq fois au moins — le tiroir de navigation, l'attente du direct,
 * ses commandes, le voile de glisser-déposer, l'anneau de progression — et
 * chaque fois on l'a corrigé sur place, par une règle de plus.
 *
 * On le traite donc comme une catégorie. C'est l'un des rares emplois
 * légitimes de « !important » : l'attribut dit que l'élément n'est pas là, et
 * aucune règle de présentation n'a d'avis à donner là-dessus.
 */
[hidden] { display: none !important; }

.btn {
  display: inline-block; padding: 9px 16px; border: 1px solid transparent;
  border-radius: 8px; background: var(--accent); color: #fff;
  font-size: 0.875rem; font-weight: 500; cursor: pointer; white-space: nowrap;
  box-shadow: var(--lift-1);
  transition: transform var(--tempo-press) var(--courbe),
              filter var(--tempo) var(--courbe),
              box-shadow var(--tempo) var(--courbe);
}
.btn:hover { filter: brightness(1.08); text-decoration: none; }
/* L'enfoncement, pas le relâchement : attendre le clic donne une interface morte. */
.btn:active { transform: scale(.975); box-shadow: none; }
.btn.ghost { background: transparent; border-color: var(--line); color: var(--text); }
.btn.ghost:hover { border-color: var(--muted); filter: none; }
.btn.danger { background: transparent; border-color: rgba(255,122,122,.4); color: var(--error); }
.btn.small { padding: 5px 11px; font-size: 0.8125rem; }

/* --- Filtres ------------------------------------------------------------- */
.filters {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end;
  padding: 16px 18px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 12px; margin-bottom: 20px;
  box-shadow: var(--lift-1);
}
.filters .field { display: flex; flex-direction: column; gap: 5px; }
.filters .field.grow { flex: 1; min-width: 220px; }
.filters label { font-size: 12px; color: var(--muted); }
.filters input, .filters select {
  padding: 8px 10px; border-radius: 7px; border: 1px solid var(--line);
  background: var(--bg); color: var(--text); font-size: 14px; min-width: 150px;
}
.filters .field.grow input { width: 100%; }

/* --- Tableaux ------------------------------------------------------------ */
table.data {
  width: 100%; border-collapse: collapse; background: var(--surface);
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  box-shadow: var(--lift-2);
  /* Les chiffres d'une colonne doivent s'aligner : sans chasse fixe, les
     volumes et les montants dansent d'une ligne à l'autre. */
  font-variant-numeric: tabular-nums;
}
table.data th, table.data td {
  padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--line);
  font-size: 14px; vertical-align: top;
}
table.data thead th {
  background: rgba(255,255,255,.045); color: var(--muted); font-weight: 600;
  font-size: 0.6875rem; text-transform: uppercase; letter-spacing: .055em;
}
table.data thead th a { color: inherit; }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr {
  transition: background-color var(--tempo) var(--courbe);
}
table.data tbody tr:hover { background: rgba(255,255,255,.05); }
table.data .num { text-align: right; }
table.data.compact th, table.data.compact td { padding: 8px 12px; font-size: 13px; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; }
.small { font-size: 12px; }

/* --- Badges et chips ----------------------------------------------------- */
.badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 12px; border: 1px solid var(--line); color: var(--muted);
}
.badge.active { border-color: rgba(74,212,160,.4); color: var(--ok); }
.badge.archived { border-color: rgba(152,160,176,.4); }
.badge.danger { border-color: rgba(255,122,122,.4); color: var(--error); }
.chip {
  display: inline-block; padding: 2px 8px; margin: 0 4px 4px 0;
  border-radius: 6px; background: #22273250; border: 1px solid var(--line);
  font-size: 12px; color: var(--muted);
}
.chip.warn { border-color: rgba(232,163,74,.4); color: var(--warm); }

/* --- Listes et fiches ---------------------------------------------------- */
.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; margin-bottom: 32px; }
@media (max-width: 860px) { .cols { grid-template-columns: 1fr; } }
.section-title {
  font-size: 0.75rem; text-transform: uppercase; letter-spacing: .055em;
  color: var(--muted); font-weight: 600; margin: 0 0 12px;
}
/* Un titre qui suit du contenu a besoin d'air : sans cela, il se lit comme la
   suite du paragraphe précédent au lieu d'ouvrir une section. La règle vaut
   aussi pour le conteneur en deux colonnes, dont les titres sont imbriqués et
   n'ont donc pas le paragraphe pour frère. */
p + .section-title, table + .section-title, form + .section-title,
dl + .section-title, ul + .section-title,
p + .cols, table + .cols, form + .cols, ul + .cols,
p + .grid, table + .grid { margin-top: 30px; }

.grid + * { margin-top: 30px; }
.list { list-style: none; padding: 0; margin: 0 0 28px; }
.list li {
  padding: 10px 0; border-bottom: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline;
}
.list li:last-child { border-bottom: 0; }
.list.compact li { padding: 7px 0; font-size: 13px; }
.list .action { color: var(--accent); font-size: 13px; min-width: 110px; }
.list .obj { flex: 1; }
.meta { color: var(--muted); font-size: 0.75rem; letter-spacing: .005em; }
.empty { color: var(--muted); font-size: 14px; padding: 18px 0; }

dl.facts { display: grid; grid-template-columns: 160px 1fr; gap: 9px 16px; margin: 0 0 28px; }
dl.facts dt { color: var(--muted); font-size: 13px; }
dl.facts dd { margin: 0; font-size: 14px; }
.notes { font-size: 14px; line-height: 1.65; }

.pager {
  display: flex; gap: 18px; align-items: center; justify-content: center;
  margin-top: 20px; font-size: 14px; color: var(--muted);
}

/* --- Formulaires --------------------------------------------------------- */
.form { max-width: 720px; }
.form fieldset {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 20px 22px 24px; margin: 0 0 20px; background: var(--surface);
  box-shadow: var(--lift-1);
}
/* Une légende s'incruste par défaut dans la bordure du fieldset. Avec un coin
   arrondi de 14 px, l'encoche commence avant que la courbe ait fini : il ne
   reste qu'un moignon de trait entre le coin et le texte, et le cadre paraît
   cassé. « float » sort la légende de ce mécanisme — la bordure redevient
   continue et le titre se pose normalement en tête de bloc. */
.form legend {
  float: left; width: 100%;
  margin: 0 0 16px; padding: 0;
  font-size: 12px; text-transform: uppercase;
  letter-spacing: .5px; color: var(--muted);
}
/* Flottante, la légende sort du flux : sans cela le premier champ remonterait
   se loger à côté d'elle. */
.form legend + * { clear: left; }
.form .field { margin-bottom: 16px; }
/* Une explication qui précède un champ ne doit pas se coller à son libellé :
   « .hint » n'a pas de marge basse, et le libellé suivant se lisait comme la
   dernière ligne du paragraphe. */
.form .hint + .field, .form .hint + label.inline { margin-top: 16px; }
.form .field:last-child { margin-bottom: 0; }
.form label { display: block; margin-bottom: 6px; font-size: 13px; color: var(--muted); }
.form input[type=text], .form input[type=email], .form input[type=url],
.form input[type=number], .form input[type=date], .form select, .form textarea {
  width: 100%; padding: 9px 12px; border-radius: 8px; border: 1px solid var(--line);
  background: var(--bg); color: var(--text); font-size: 14px; font-family: inherit;
}
.form ul { list-style: none; padding: 0; margin: 0;
           display: flex; flex-wrap: wrap; gap: 6px 16px; }
.form ul li label { display: inline-flex; align-items: center; gap: 6px;
                    margin: 0; color: var(--text); font-size: 14px; }
.hint { margin: 5px 0 0; font-size: 0.75rem; line-height: 1.6; color: var(--muted); letter-spacing: .005em; }
.form-actions { display: flex; gap: 12px; align-items: center; }

/* --- Bandeaux d'information --------------------------------------------- */
.notice {
  padding: 14px 18px; border-radius: 10px; margin-bottom: 24px; font-size: 14px;
  background: var(--surface); border: 1px solid var(--line); line-height: 1.6;
}
.notice.warn { border-color: rgba(232,163,74,.35); background: rgba(232,163,74,.06); }
.notice strong { color: var(--text); }
.key-list { list-style: none; padding: 10px 0 0; margin: 0; }
.key-list li { padding: 2px 0; color: var(--muted); }

/* --- Dépôt de fichiers --------------------------------------------------- */
.uploader-actions {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 18px;
}
.global-progress { display: flex; align-items: center; gap: 14px; margin: 18px 0; }
.global-bar-track {
  flex: 1; height: 8px; border-radius: 999px; background: var(--bg);
  border: 1px solid var(--line); overflow: hidden;
}
.global-bar { height: 100%; width: 0; background: var(--accent); transition: width .3s ease; }

.upload-list { list-style: none; padding: 0; margin: 0 0 24px; }
.upload-row {
  display: grid; grid-template-columns: 1fr 90px 140px 90px;
  gap: 14px; align-items: center; padding: 9px 0;
  border-bottom: 1px solid var(--line); font-size: 13px;
}
.upload-path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
               font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.upload-size, .upload-state { color: var(--muted); text-align: right; }
.upload-bar {
  height: 6px; border-radius: 999px; background: var(--bg);
  border: 1px solid var(--line); overflow: hidden; display: block;
}
/* Un seul vert du début à la fin : la barre qui se remplit et la barre pleine
   disent la même chose, et deux verts voisins se liraient comme une nuance
   porteuse de sens qu'elle n'aurait pas. */
.upload-bar > span {
  display: block; height: 100%; width: 0; background: var(--vert-systeme);
  transition: width .25s ease;
}
.upload-row.done .upload-bar > span { background: var(--vert-systeme); }
.upload-row.done .upload-state { color: var(--vert-systeme); }
.upload-row.error .upload-bar > span { background: var(--error); }
.upload-row.error .upload-state { color: var(--error); }

.mini-bar {
  display: inline-block; width: 90px; height: 6px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--line); overflow: hidden;
  vertical-align: middle; margin-right: 8px;
}
.mini-bar > span { display: block; height: 100%; background: var(--accent); }

/* --- Statuts ------------------------------------------------------------- */
.badge.valid, .badge.passed, .badge.complete, .badge.qc_done,
.badge.received { border-color: rgba(74,212,160,.4); color: var(--ok); }
.badge.invalid, .badge.failed, .badge.error, .badge.aborted,
.badge.abandoned { border-color: rgba(255,122,122,.4); color: var(--error); }
.badge.warning, .badge.warnings, .badge.warn,
.badge.purged { border-color: rgba(232,163,74,.4); color: var(--warm); }
.badge.uploading, .badge.qc_running, .badge.running, .badge.qc_queued,
.badge.pending_qc { border-color: rgba(125,123,245,.4); color: var(--accent); }
.badge.info { border-color: var(--line); color: var(--muted); }

tr.finding.error td { background: rgba(255,122,122,.04); }
tr.finding.warning td { background: rgba(232,163,74,.03); }

.small-metric { font-size: 15px; padding-top: 6px; }
.metric-sub { font-size: 16px; color: var(--muted); }

/* --- Fiches CPL ---------------------------------------------------------- */
.cpl-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; padding: 16px 18px; margin-bottom: 14px;
}
.cpl-card h3 { margin: 0 0 12px; font-size: 14px; font-weight: 500; }
dl.facts.tight { grid-template-columns: 130px 1fr; gap: 5px 14px; margin: 0; }
dl.facts.tight dt, dl.facts.tight dd { font-size: 13px; }

.inline-form {
  display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap;
  padding: 16px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 10px;
}
.inline-form label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 5px; }
.inline-form select {
  padding: 8px 10px; border-radius: 7px; border: 1px solid var(--line);
  background: var(--bg); color: var(--text); font-size: 14px; min-width: 260px;
}

details summary { cursor: pointer; }
code.small { display: block; margin-top: 6px; padding: 7px 9px; background: var(--bg);
             border-radius: 6px; color: var(--muted); word-break: break-all; }

/* --- Vignettes ----------------------------------------------------------- */
.previews { margin-bottom: 32px; }
.preview-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px;
}
.preview-strip figure { margin: 0; }
.preview-strip img {
  width: 100%; aspect-ratio: 16 / 9; object-fit: cover; display: block;
  border-radius: 8px; border: 1px solid var(--line); background: var(--bg);
}
.preview-strip figure.poster img { border-color: var(--accent); }
.preview-strip figcaption {
  margin-top: 6px; font-size: 11px; color: var(--muted); text-align: center;
}
.poster-large {
  width: 100%; max-width: 460px; border-radius: 10px;
  border: 1px solid var(--line); margin-bottom: 20px; display: block;
}

/* --- Parc et échéances --------------------------------------------------- */
.tile.alerte { border-color: rgba(255,122,122,.4); }
.tile.alerte .metric { color: var(--error); }
.badge.expire, .badge.expired, .badge.revoked { border-color: rgba(255,122,122,.4); color: var(--error); }
.badge.pending { border-color: rgba(125,123,245,.4); color: var(--accent); }
.badge.inactive, .badge.replaced { border-color: var(--line); color: var(--muted); }
label.inline { display: inline-flex; align-items: center; gap: 8px; margin-top: 10px;
               font-size: 14px; color: var(--text); }

/* Motifs de refus d'une émission : lisibles en pile, jamais tronqués — c'est
   ce que l'opérateur doit corriger avant de réessayer. */
ul.reasons { margin: 4px 0 0; padding-left: 18px; }
ul.reasons li { font-size: 12px; color: var(--muted); line-height: 1.5; }
p.group { margin: 14px 0 4px; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }

/* Sélection de destinataires : une ligne par case. Alignées côte à côte, les
   salles d'un même site deviennent illisibles dès qu'elles portent un badge. */
.choicelist label.inline { display: flex; width: 100%; margin-top: 6px; }

/* Certificat public affiché tel quel : à copier, donc jamais reformaté ni tronqué. */
pre.pem {
  white-space: pre-wrap; word-break: break-all; max-height: 320px; overflow: auto;
  padding: 14px 16px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface); color: var(--muted); line-height: 1.45;
}

/* --- Page publique de visionnage -----------------------------------------
   Volontairement dépouillée : aucun élément de l'outil interne n'a sa place
   devant un journaliste ou un sélectionneur de festival. */
body.watch { display: flex; min-height: 100vh; align-items: center; justify-content: center; }
.watch-shell { width: min(1100px, 94vw); padding: 32px 0; }
.watch-head { margin-bottom: 18px; }
.watch-head h1 { font-size: 22px; margin: 0 0 4px; }
.watch-card {
  max-width: 520px; margin: 0 auto; padding: 32px 34px;
  border: 1px solid var(--line); border-radius: 14px; background: var(--surface);
}
.watch-card h1 { font-size: 20px; margin: 0 0 12px; }
.watch-error { color: #ff6b6b; font-size: 13px; margin: 6px 0 0; }

.watch-player { position: relative; aspect-ratio: 16 / 9; background: #000; border-radius: 12px; overflow: hidden; }
.watch-player iframe { width: 100%; height: 100%; border: 0; display: block; }

/* Le calque ne doit jamais intercepter un clic destiné aux commandes. */
.watch-mark {
  position: absolute; pointer-events: none; user-select: none;
  font-size: 13px; line-height: 1.35; color: rgba(255,255,255,.62);
  text-shadow: 0 1px 3px rgba(0,0,0,.9);
  transition: top 1.2s ease, left 1.2s ease;
}
.watch-mark span { display: block; }
.watch-mark-sub { font-size: 11px; color: rgba(255,255,255,.5); }

/* ==========================================================================
   Accessibilité des réglages système
   Réduire le mouvement ne veut pas dire supprimer le retour : il reste, plus
   doux. Réduire la transparence ne veut pas dire aplatir : la matière devient
   opaque et garde sa fonction de séparation.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  /* On garde l'enfoncement, qui informe, en retirant le déplacement. */
  .btn:active { transform: none; filter: brightness(.92); }
}

@media (prefers-reduced-transparency: reduce) {
  .sidebar {
    background: #13161c;
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
}

@media (prefers-contrast: more) {
  :root { --muted: #c3c9d6; --line: #47506a; }
  .nav a[aria-current="page"] { box-shadow: inset 0 0 0 1px var(--accent); }
  table.data th, table.data td { border-bottom-color: var(--line); }
  .badge { border-width: 1px; color: var(--text); }
}

/* Le filigrane d'un screener défile en continu : c'est exactement le genre de
   mouvement lent et perpétuel qui gêne. Il se fige, sans disparaître. */
@media (prefers-reduced-motion: reduce) {
  .watch-mark { transition: none; }
}

/* --- Marque -------------------------------------------------------------
   Le logo est un dessin noir sur fond transparent. Sur une interface sombre,
   une simple inversion suffit : le noir devient blanc et les pixels
   transparents le restent. Aucun mode de fusion n'est nécessaire — j'en avais
   prévu un avant de vérifier le fichier, il aurait été nuisible ici.

   Le trait est fin et manuscrit : sous 130 px de large il se referme et les
   pattes disparaissent. C'est le plancher, pas une préférence. */
.brand {
  display: block; margin: 4px 12px 22px; color: var(--text);
  text-decoration: none;
}
.brand:hover { text-decoration: none; }
.brand:hover .brand-logo { opacity: 1; }

.brand-logo {
  display: block; width: 100%; min-width: 130px; max-width: 174px; height: auto;
  filter: invert(1);
  opacity: .92;
  transition: opacity var(--tempo) var(--courbe);
}

/* Le nom du produit sous la marque de maison : ce sont deux choses
   différentes, et les confondre reviendrait à faire croire que l'outil est
   l'entreprise. */
.brand-product {
  display: block; margin-top: 7px;
  font-size: 0.8125rem; font-weight: 500; letter-spacing: .02em;
  color: var(--muted);
}

@media (max-width: 900px) {
  .brand { margin: 0 14px 0 0; flex: none; }
  .brand-logo { width: 132px; min-width: 0; }
  .brand-product { display: none; }
}

/* Sur la page de connexion, la marque est ce qu'on voit en premier. */
.card .brand-logo { max-width: 100%; margin: 0 auto 18px; }

/* --- Points de vigilance -------------------------------------------------
   Une phrase par ligne, un point de couleur pour la gravité, une flèche pour
   dire que ça mène quelque part. Pas de compteur isolé : « 3 » n'apprend rien
   tant qu'on ne sait pas 3 quoi, ni si c'est grave. */
.attention { list-style: none; padding: 0; margin: 0 0 34px; display: grid; gap: 8px; }

.attention-item a {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 12px;
  padding: 13px 16px; border-radius: 11px;
  background: var(--surface); border: 1px solid var(--line);
  color: var(--text); font-size: 0.9375rem; line-height: 1.45;
  box-shadow: var(--lift-1);
  transition: transform var(--tempo-press) var(--courbe),
              border-color var(--tempo) var(--courbe),
              box-shadow var(--tempo) var(--courbe);
}
.attention-item a:hover { text-decoration: none; border-color: rgba(255,255,255,.24); box-shadow: var(--lift-2); }
.attention-item a:active { transform: scale(.995); box-shadow: var(--lift-1); }

.attention-dot {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--muted);
  box-shadow: 0 0 0 3px rgba(152,160,176,.14);
}
.attention-item.urgent .attention-dot {
  background: var(--error); box-shadow: 0 0 0 3px rgba(255,122,122,.16);
}
.attention-item.attention .attention-dot {
  background: var(--warm); box-shadow: 0 0 0 3px rgba(232,163,74,.16);
}
.attention-dot.ok { background: var(--ok); box-shadow: 0 0 0 3px rgba(74,212,160,.16); }

.attention-item.urgent a { border-color: rgba(255,122,122,.28); }
.attention-item.attention a { border-color: rgba(232,163,74,.24); }

.attention-go { color: var(--muted); font-size: 1rem; transition: transform var(--tempo) var(--courbe); }
.attention-item a:hover .attention-go { transform: translateX(3px); }

/* Tout va bien : on le dit une fois, en clair, sans en faire un tableau. */
.all-clear {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 34px; padding: 15px 18px; border-radius: 11px;
  background: rgba(74,212,160,.05); border: 1px solid rgba(74,212,160,.2);
  color: var(--muted); font-size: 0.9375rem;
}

/* Ce qui avance seul : plus discret, car rien n'est attendu du lecteur. */
.running {
  list-style: none; padding: 0; margin: 0 0 34px;
  display: flex; flex-wrap: wrap; gap: 8px;
}
.running li a {
  display: inline-block; padding: 6px 13px; border-radius: 999px;
  background: rgba(255,255,255,.035); border: 1px solid var(--line);
  color: var(--muted); font-size: 0.8125rem;
  transition: color var(--tempo) var(--courbe), border-color var(--tempo) var(--courbe);
}
.running li a:hover { color: var(--text); border-color: rgba(255,255,255,.24); text-decoration: none; }

/* Un vide qui explique vaut mieux qu'un vide qui constate. */
.empty { color: var(--muted); font-size: 0.875rem; padding: 16px 0; line-height: 1.6; }

/* --- Recherche transversale ---------------------------------------------
   Placée au-dessus de la navigation : quand on ne sait pas dans quelle section
   chercher, c'est précisément qu'il ne faut pas commencer par en choisir une. */
/* --- Cases à cocher et boutons radio -------------------------------------
   Laissés au navigateur, ils arrivent en 13 px, désalignés de leur libellé et
   étrangers au reste. La cible reste petite au clavier comme au doigt. */
.field input[type="checkbox"],
.field input[type="radio"],
ul.field-choices input[type="checkbox"],
ul.field-choices input[type="radio"] {
  width: 17px; height: 17px; flex: none;
  margin: 0 9px 0 0; vertical-align: -3px;
  accent-color: var(--accent);
  cursor: pointer;
}
.field label > input[type="checkbox"],
.field label > input[type="radio"] { vertical-align: -3px; }

/* Une liste de cases n'est pas une liste à puces : la puce doublerait la case. */
.field ul { list-style: none; padding: 0; margin: 0; display: grid; gap: 6px; }
.field ul label { display: flex; align-items: center; cursor: pointer; }


.notes-barre {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 14px;
}
.notes-barre .btn { margin-left: auto; }
table.data tr.fait td { opacity: .66; }
table.data tr.fait td:nth-child(2) { text-decoration: line-through; }

/* --- Notes de relecture --------------------------------------------------
   Sous le lecteur et non à côté : sur un téléphone il n'y a pas de côté, et
   c'est souvent là qu'un réalisateur regarde une première fois. */
.notes { max-width: 1100px; margin: 26px auto 0; padding: 0 4px; }
.notes-head { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; }
.notes-head h2 { margin: 0; font-size: 1.05rem; font-weight: 600; color: var(--text); }
.notes-head .btn { margin-left: auto; }
.notes-aide { margin: 6px 0 16px; font-size: 0.8125rem; color: var(--muted); max-width: 62ch; }

.notes-form {
  margin: 0 0 18px; padding: 16px 18px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line);
}
.notes-position { margin: 0 0 12px; font-size: 0.8125rem; color: var(--muted); }
.notes-position strong { color: var(--accent); font-variant-numeric: tabular-nums; }
.notes-form label { display: block; margin-bottom: 5px; font-size: 13px; color: var(--muted); }
.notes-form input, .notes-form textarea {
  width: 100%; margin-bottom: 12px; padding: 9px 11px;
  border-radius: 8px; border: 1px solid var(--line);
  background: rgba(0,0,0,.28); color: var(--text); font: inherit; font-size: 15px;
}
.notes-actions { display: flex; gap: 10px; }
.notes-erreur { margin: 10px 0 0; font-size: 0.8125rem; color: var(--error); }

.notes-liste { list-style: none; padding: 0; margin: 0; display: grid; gap: 6px; }
.note {
  display: grid; grid-template-columns: auto 1fr auto auto; align-items: baseline;
  gap: 12px; padding: 10px 14px; border-radius: 10px;
  background: var(--surface); border: 1px solid var(--line);
}
.note-tc {
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 0.8125rem;
  color: var(--accent); background: none; border: 0; padding: 0; cursor: pointer;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.note-tc:hover { text-decoration: underline; }
.note-corps { color: var(--text); font-size: 0.9375rem; overflow-wrap: anywhere; }
.note-auteur { font-size: 0.75rem; color: var(--muted); white-space: nowrap; }

/* Une note traitée reste lisible : elle documente ce qui a été demandé, et
   l'effacer priverait le relecteur de la réponse à sa remarque. */
.note.fait { opacity: .72; }
.note.fait .note-corps { text-decoration: line-through; text-decoration-color: var(--muted); }
.note-fait { color: var(--ok, #4ad4a0); font-weight: 700; }

.notes-vide { margin: 0; font-size: 0.875rem; color: var(--muted); }

@media (max-width: 640px) {
  .note { grid-template-columns: auto 1fr; }
  .note-auteur, .note-fait { grid-column: 2; }
}

/* --- Remise du lien ------------------------------------------------------
   Le lien ne s'affiche qu'une fois : les deux gestes qui l'emportent doivent
   être là, sous les yeux, au moment où il est lisible. */
.livraison { margin: 14px 0 12px; gap: 10px; flex-wrap: wrap; }
.livraison .btn.fait { background: rgba(74,212,160,.16); border-color: rgba(74,212,160,.4); }
.apercu-courriel { margin-top: 12px; }
.apercu-courriel summary { cursor: pointer; font-size: 0.8125rem; color: var(--muted); }
.apercu-courriel summary:hover { color: var(--text); }
.apercu-courriel pre {
  margin: 8px 0; padding: 12px 14px; border-radius: 8px;
  background: rgba(0,0,0,.22); border: 1px solid var(--line);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

.search { position: relative; margin: 0 8px 16px; }
.search input {
  width: 100%; padding: 8px 30px 8px 11px;
  border-radius: 8px; border: 1px solid var(--line);
  background: rgba(0,0,0,.22); color: var(--text);
  font-family: inherit; font-size: 0.8125rem;
  transition: border-color var(--tempo) var(--courbe), background-color var(--tempo) var(--courbe);
}
.search input::placeholder { color: #8f8ab8; }
.search input:focus { background: rgba(0,0,0,.34); border-color: rgba(255,255,255,.24); }
.search input:focus + kbd { opacity: 0; }
.search kbd {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  padding: 1px 5px; border-radius: 4px; pointer-events: none;
  border: 1px solid var(--line); background: rgba(255,255,255,.04);
  font: inherit; font-size: 0.6875rem; color: #8f8ab8;
  transition: opacity var(--tempo) var(--courbe);
}
/* L'ordre forcé ne vaut que pour la rangée horizontale de la tablette. Dans
   le tiroir du téléphone, il rejetterait la recherche derrière la
   déconnexion, loin de la place que le regard lui attribue. */
@media (min-width: 761px) and (max-width: 900px) {
  .search { order: 3; flex: 1 1 100%; margin: 8px 0 0; }
}
@media (max-width: 900px) {
  .search kbd { display: none; }
}

.results { list-style: none; padding: 0; margin: 0 0 6px; display: grid; gap: 6px; }
.results li a {
  display: grid; gap: 2px; padding: 11px 15px;
  border-radius: 10px; background: var(--surface); border: 1px solid var(--line);
  color: var(--text); box-shadow: var(--lift-1);
  transition: transform var(--tempo-press) var(--courbe),
              border-color var(--tempo) var(--courbe);
}
.results li a:hover { text-decoration: none; border-color: rgba(255,255,255,.24); }
.results li a:active { transform: scale(.995); }
.results-title { font-size: 0.9375rem; }
.results-detail { font-size: 0.75rem; color: var(--muted); }

/* --- Icônes de navigation ------------------------------------------------ */
.ic { width: 17px; height: 17px; flex: none; opacity: .72; }
.nav a { display: flex; align-items: center; gap: 10px; }
.nav a:hover .ic, .nav a[aria-current="page"] .ic { opacity: 1; }

/* --- Réglages ------------------------------------------------------------ */
.settings { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }
.settings-card {
  display: grid; gap: 3px; padding: 16px 18px; position: relative;
  border-radius: 12px; background: var(--surface); border: 1px solid var(--line);
  color: var(--text); box-shadow: var(--lift-1);
  transition: transform var(--tempo-press) var(--courbe),
              border-color var(--tempo) var(--courbe),
              box-shadow var(--tempo) var(--courbe);
}
.settings-card:hover { text-decoration: none; border-color: rgba(255,255,255,.24); box-shadow: var(--lift-2); }
.settings-card:active { transform: scale(.995); box-shadow: var(--lift-1); }
.settings-title { font-size: 0.9375rem; font-weight: 500; }
.settings-desc { font-size: 0.8125rem; color: var(--muted); line-height: 1.5; padding-right: 28px; }
.settings-count {
  position: absolute; top: 15px; right: 16px;
  font-size: 0.75rem; color: var(--muted); font-variant-numeric: tabular-nums;
}

/* --- Que voulez-vous faire ? ---------------------------------------------
   Des verbes, en tête d'accueil. « Titres » ou « KDM » sont des objets : ils
   supposent qu'on sache déjà lequel manipuler. On arrive avec une intention. */
.tasks {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px; margin-bottom: 30px;
}
.task {
  --teinte: 125, 123, 245;
  display: grid; gap: 4px; padding: 18px 20px;
  border-radius: 13px; color: var(--text);
  background: linear-gradient(160deg, rgba(var(--teinte), .12), rgba(var(--teinte), .03));
  border: 1px solid rgba(var(--teinte), .24); box-shadow: var(--lift-1);
  transition: transform var(--tempo-press) var(--courbe),
              border-color var(--tempo) var(--courbe), box-shadow var(--tempo) var(--courbe);
}
.task:hover { text-decoration: none; border-color: rgba(var(--teinte), .5); box-shadow: var(--lift-2); }
.task:active { transform: scale(.99); box-shadow: var(--lift-1); }
.task-ic { width: 20px; height: 20px; color: rgb(var(--teinte)); margin-bottom: 4px; }

/* Diffusion en indigo, clés en ambre — la couleur de la lumière de projection —
   et réception en vert d'eau. Trois domaines, trois températures. */
.task.t-diffusion { --teinte: 125, 123, 245; }
.task.t-cles { --teinte: 232, 163, 74; }
.task.t-reception { --teinte: 74, 212, 160; }
.task-title { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.005em; }
.task-detail { font-size: 0.8125rem; color: var(--muted); line-height: 1.5; }

/* --- Parcours guidé ------------------------------------------------------ */
.steps {
  list-style: none; display: flex; flex-wrap: wrap; gap: 8px 20px;
  padding: 0; margin: 0 0 18px;
}
.steps li { display: flex; align-items: center; gap: 8px; color: #8f8ab8; font-size: 0.8125rem; }
.steps-num {
  display: grid; place-items: center; width: 22px; height: 22px; flex: none;
  border-radius: 50%; border: 1px solid var(--line); font-size: 0.6875rem;
}
.steps li.done { color: var(--muted); }
.steps li.done .steps-num { border-color: rgba(74,212,160,.45); color: var(--ok); }
.steps li.now { color: var(--text); }
.steps li.now .steps-num {
  border-color: var(--accent); color: var(--accent); background: rgba(125,123,245,.12);
}

/* Dire à chaque étape ce qu'elle engage : c'est de ne pas savoir si une action
   se rattrape qu'on reste figé. Une seule étape parle fort — si tout est
   signalé comme grave, plus rien ne l'est. */
.engagement {
  margin: 0 0 26px; padding: 11px 15px; border-radius: 9px;
  font-size: 0.8125rem; line-height: 1.55;
}
.engagement.libre { background: rgba(255,255,255,.03); border: 1px solid var(--line); color: var(--muted); }
.engagement.definitif {
  background: rgba(232,163,74,.07); border: 1px solid rgba(232,163,74,.32); color: var(--text);
}

.guide { max-width: 640px; }
.guide fieldset { margin-bottom: 22px; }
.legend-opt { text-transform: none; letter-spacing: 0; color: #8f8ab8; }

label.inline.pick {
  align-items: flex-start; gap: 11px; padding: 12px 14px; margin-top: 8px;
  border: 1px solid var(--line); border-radius: 10px; width: 100%;
  transition: border-color var(--tempo) var(--courbe), background-color var(--tempo) var(--courbe);
}
label.inline.pick:hover { border-color: rgba(255,255,255,.24); background: rgba(255,255,255,.02); }
label.inline.pick:has(input:checked) {
  border-color: rgba(125,123,245,.5); background: rgba(125,123,245,.07);
}
label.inline.pick input { margin-top: 3px; flex: none; }
.pick-detail { display: block; margin-top: 4px; font-size: 0.8125rem; color: var(--muted); line-height: 1.55; }

.recap { padding: 20px 22px; border-radius: 12px; background: var(--surface);
         border: 1px solid var(--line); box-shadow: var(--lift-1); margin-bottom: 22px; }
.recap dl.facts { margin: 0; }

/* --- Prérequis ----------------------------------------------------------- */
.checks { list-style: none; padding: 0; margin: 0 0 26px; display: grid; gap: 10px; }
.checks li {
  display: flex; gap: 13px; padding: 15px 18px; border-radius: 11px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--lift-1);
}
.checks li.ok { border-color: rgba(74,212,160,.3); }
.checks-mark { font-size: 0.9375rem; line-height: 1.5; flex: none; }
.checks li.ok .checks-mark { color: var(--ok); }
.checks li.todo .checks-mark { color: var(--warm); }
.checks-body { display: grid; gap: 6px; justify-items: start; }
.checks-label { font-size: 0.9375rem; }
.checks-manque { font-size: 0.8125rem; color: var(--muted); line-height: 1.55; }

/* ==========================================================================
   Adoucissements
   Un outil qu'on regarde six heures par jour n'a pas besoin d'être austère
   pour être sérieux. Rien ici n'ajoute d'information : ce sont des angles plus
   ronds, des surfaces qui accrochent un peu de lumière, et un accent qui a une
   température plutôt qu'une valeur.
   ========================================================================== */

/* Un halo très large, immobile, qui évite au fond d'être parfaitement plat.
   Fixe et sans animation : une lueur qui bouge derrière du texte fatigue. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -3; pointer-events: none;
  background:
    radial-gradient(62rem 42rem at 2% 22%, rgba(88, 132, 255, .46), transparent 66%),
    radial-gradient(54rem 38rem at 26% -4%, rgba(150, 96, 246, .40), transparent 64%),
    radial-gradient(42rem 30rem at 0% 54%, rgba(226, 104, 190, .26), transparent 62%),
    radial-gradient(46rem 32rem at 70% -8%, rgba(96, 140, 255, .16), transparent 66%),
    radial-gradient(58rem 38rem at 92% 96%, rgba(232, 163, 74, .12), transparent 68%);
}

/* Trame de points. Fixe — une houle animée est charmante dix secondes et
   épuisante à onze heures du matin, et le mouvement en vision périphérique est
   précisément ce que l'œil ne sait pas ignorer.

   Elle n'est pas posée uniformément : un masque la révèle là où la lueur passe
   et l'efface ailleurs. La trame ne se lit donc jamais comme du papier
   millimétré — elle donne l'impression que la lumière accroche une grille, ce
   qui est l'effet recherché et non un quadrillage de fond.

   En dégradés CSS : aucun fichier à charger, et net à toutes les densités
   d'écran. */
html::before {
  content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background-image:
    radial-gradient(circle at center, rgba(214, 220, 255, .30) 1px, transparent 1.4px);
  background-size: 26px 26px;
  -webkit-mask-image:
    radial-gradient(58rem 40rem at 4% 20%, #000 0%, rgba(0,0,0,.55) 42%, transparent 72%),
    radial-gradient(46rem 30rem at 90% 94%, rgba(0,0,0,.42) 0%, transparent 68%);
  mask-image:
    radial-gradient(58rem 40rem at 4% 20%, #000 0%, rgba(0,0,0,.55) 42%, transparent 72%),
    radial-gradient(46rem 30rem at 90% 94%, rgba(0,0,0,.42) 0%, transparent 68%);
}

/* Grain. Un dégradé large sans grain se voit : il se casse en bandes sur un
   écran 8 bits, et il a l'air imprimé. Une trame très fine par-dessus suffit à
   le rendre continu — c'est ce qui sépare une lueur d'un aplat dégradé. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: .38; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* Le bouton principal porte la lumière plutôt qu'un aplat. */
.btn { background-image: linear-gradient(175deg, rgba(255,255,255,.14), rgba(255,255,255,0)); }
.btn.ghost, .btn.danger { background-image: none; }

/* Les surfaces attrapent un filet de clarté sur leur arête haute, comme un
   objet éclairé par le dessus. */
.tile, table.data, .form fieldset, .filters, .recap, .checks li,
.settings-card, .attention-item a, .results li a, .card {
  background-image: linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,.012) 46%);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
}

/* Angles : un cran plus généreux, sans arrondir au point de faire jouet. */
.tile, table.data, .form fieldset, .filters, .recap, .card { border-radius: 14px; }
.checks li, .settings-card, .attention-item a, .results li a, .task { border-radius: 13px; }
.btn { border-radius: 9px; }

/* Le survol d'une ligne de tableau se teinte au lieu de virer au gris. */
table.data tbody tr:hover { background: rgba(125,123,245,.055); }

/* Les pastilles d'état gagnent un fond très léger : un simple liseré se perd
   sur un fond chaud. */
.badge.active, .badge.valid { background: rgba(74,212,160,.09); }
.badge.danger, .badge.invalid { background: rgba(255,122,122,.09); }
.badge.warn { background: rgba(232,163,74,.09); }

/* Le nom de la marque, dans la colonne, respire un peu plus. */
.brand-product { letter-spacing: .06em; text-transform: uppercase; font-size: 0.6875rem; }

@media (prefers-reduced-transparency: reduce) { body::before, html::before { display: none; } }
@media (prefers-contrast: more) {
  body::before { display: none; }
  .tile, table.data, .form fieldset, .filters, .recap, .checks li,
  .settings-card, .attention-item a, .results li a, .card { background-image: none; }
}

/* Le grain et la lueur sont un décor : ils disparaissent dès que le système
   demande moins de transparence ou plus de contraste, et les surfaces
   redeviennent opaques pour que rien ne dépende d'eux. */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
  body::after, html::before { display: none; }
  :root { --surface: #1e1d4a; }
  .tile, table.data, .form fieldset, .filters, .recap, .checks li,
  .settings-card, .attention-item a, .results li a, .card {
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
}

/* --- Marche à suivre ----------------------------------------------------- */
.howto { list-style: none; padding: 0; margin: 0 0 26px; display: grid; gap: 10px; counter-reset: pas; }
.howto li {
  display: flex; gap: 14px; padding: 15px 18px; border-radius: 13px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--lift-1);
  background-image: linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,.012) 46%);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
}
.howto-num {
  display: grid; place-items: center; width: 25px; height: 25px; flex: none;
  border-radius: 50%; font-size: 0.75rem; font-variant-numeric: tabular-nums;
  color: var(--accent); background: rgba(125,123,245,.14);
  box-shadow: inset 0 0 0 1px rgba(125,123,245,.3);
}
.howto li > span:last-child { display: grid; gap: 4px; }
.howto-detail { font-size: 0.8125rem; color: var(--muted); line-height: 1.6; }

/* --- Page publique de visionnage, suite ----------------------------------
   C'est la seule page que voit un journaliste ou un sélectionneur. Elle porte
   donc la marque : sans elle, le destinataire ne sait pas de qui vient le lien. */
.watch-brand { display: block; width: 156px; margin: 0 0 26px; opacity: .85; }
.watch-brand img { width: 100%; height: auto; filter: invert(1); display: block; }

.watch-head h1 { font-size: 1.75rem; line-height: 1.15; letter-spacing: -0.02em; }
.watch-version { margin: 4px 0 0; color: var(--muted); font-size: 0.875rem; }

.watch-player { box-shadow: var(--lift-3); }
.watch-player-vide {
  display: grid; place-items: center; text-align: center; padding: 32px;
  background: var(--surface); border: 1px solid var(--line);
  background-image: linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,.012) 46%);
}
.watch-vide-titre { margin: 0 0 6px; font-size: 1rem; color: var(--text); }
.watch-vide-detail { margin: 0; font-size: 0.875rem; color: var(--muted); line-height: 1.6; max-width: 34ch; }

.watch-foot {
  display: flex; flex-wrap: wrap; gap: 6px 18px;
  margin-top: 18px; font-size: 0.8125rem; color: var(--muted);
}
.watch-foot-note { color: #8f8ab8; }

/* --- Page publique de dépôt ----------------------------------------------
   Le déposant n'est ni client ni utilisateur de l'outil : il nous rend un
   service, souvent en fin de journée et sans avoir demandé à être là. La page
   tient en un écran, ne pose aucune question, et ne montre jamais ce qui a
   déjà été déposé. */
/* Le logo et le contenu partagent la même colonne : détachés, l'un flotte
   au loin pendant que l'autre est centré, et la page paraît cassée. */
.page-depot .watch-shell { width: min(620px, 92vw); }
.depot { margin: 0 auto; }
.depot h1 { font-size: 1.75rem; line-height: 1.15; letter-spacing: -0.02em; margin: 0 0 12px; }
.depot-mot {
  margin: 0 0 18px; padding: 14px 18px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line);
  font-size: 15px; line-height: 1.6; box-shadow: var(--lift-1);
}
.depot-aide { margin: 0 0 24px; color: var(--muted); font-size: 14px; line-height: 1.65; }
.depot-note { margin: 24px 0 0; color: var(--muted); font-size: 12.5px; line-height: 1.6; }

/* La zone de dépôt est un objet, pas deux boutons perdus : c'est elle qui dit
   au déposant où lâcher ses fichiers, et elle doit se voir sans être lue. */
.depot-zone {
  border: 1.5px dashed var(--line); border-radius: 14px;
  padding: 34px 26px; text-align: center; background: var(--surface);
  transition: border-color .15s ease, background .15s ease;
}
.depot-zone.survol { border-color: var(--accent); background: rgba(122, 108, 255, .07); }
.depot-zone p { margin: 0 0 16px; color: var(--muted); font-size: 14px; }
.depot-zone .uploader-actions { justify-content: center; margin: 0; }

.depot-fini {
  margin: 18px 0 0; padding: 14px 18px; border-radius: 12px;
  background: rgba(64, 190, 130, .10); border: 1px solid rgba(64, 190, 130, .35);
  font-size: 14px; line-height: 1.6;
}
.depot .global-progress, .depot .upload-list { margin-top: 18px; }
.depot .form-actions { margin-top: 18px; justify-content: center; }

.lien-frais { margin: 10px 0 0; word-break: break-all; }
.lien-frais code { font-size: 13px; }

/* --- Pages d'accès sur fond vidéo ----------------------------------------
   Repris de la page « Material Request » du site : des calques vidéo qui se
   relaient en fondu, un voile flouté par-dessus, et une nappe de points animée
   au-dessus encore.

   C'est le flou qui fait tenir l'ensemble. Sans lui, un formulaire posé au
   centre devient illisible dès qu'un visage clair passe derrière, et aucun
   assombrissement ne rattrape cela sans éteindre l'image. Flouté, le fond perd
   son détail mais garde sa couleur et son mouvement : il redevient un décor,
   et le texte peut se poser n'importe où. */
body.gate { background: #05050b; }

.gate-bg { position: fixed; inset: 0; z-index: 0; background: #05050b; }
.gate-slot {
  position: absolute; inset: 0; opacity: 0;
  transition: opacity .55s ease;
  background: #05050b center / cover no-repeat;
}
.gate-slot.on { opacity: 1; }
.gate-slot video {
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .5s ease;
}
/* La vidéo ne se montre qu'une fois prête : révélée trop tôt, elle affiche un
   rectangle noir à la place de l'affiche qu'elle vient remplacer. */
.gate-slot video.joue, .gate-slot.joue video { opacity: 1; }

.gate-bg::after {
  content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
  background: rgba(8, 8, 10, .68);
  backdrop-filter: blur(16px) saturate(110%);
  -webkit-backdrop-filter: blur(16px) saturate(110%);
}
/* Là où le flou d'arrière-plan n'existe pas, le voile seul doit suffire : sans
   ce repli, la page tomberait sur une vidéo nette et un texte illisible. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .gate-bg::after { background: rgba(8, 8, 10, .80); }
}

/* Nappe de points. Masquée en haut et en bas pour qu'elle naisse et meure
   sans bord franc. */
.gate-dots {
  position: fixed; inset: 0; z-index: 1; display: block;
  width: 100%; height: 100%; pointer-events: none;
  mask-image: linear-gradient(transparent 0%, black 14%, black 86%, transparent 100%);
  -webkit-mask-image: linear-gradient(transparent 0%, black 14%, black 86%, transparent 100%);
}

body.gate main.auth {
  position: relative; z-index: 2;
  display: grid; place-items: center;
  min-height: 100vh; min-height: 100svh;
  padding: clamp(2rem, 6vh, 4rem) clamp(1.4rem, 4vw, 3.4rem);
}

.gate-card {
  background: none; border: 0; box-shadow: none;
  width: min(24rem, 100%); margin: 0; padding: 0;
  color: #fff;
  /* L'ombre porte le contraste, et elle seule : elle épouse les lettres, donc
     elle ne peut pas dessiner de zone. C'est le seul renfort qui reste
     invisible sur un plan sombre et suffisant sur un visage en pleine
     lumière — le cas le plus dur, et le seul qui compte. */
  text-shadow: 0 1px 4px rgba(0, 0, 0, .85), 0 0 18px rgba(0, 0, 0, .62);
  /* Toutes les surfaces de l'outil portent un flou d'arrière-plan. Ici il
     s'appliquait une seconde fois, sur le rectangle de la carte et sur un fond
     déjà flouté : le résultat était un carré aux arêtes nettes au milieu d'une
     image sans bord. « background: none » ne l'effaçait pas — le filtre n'est
     pas une propriété de fond. */
  backdrop-filter: none; -webkit-backdrop-filter: none;
  border-radius: 0;
  /* Le fond et la nappe sont positionnés ; la carte, elle, ne l'était pas, et
     un élément positionné se peint au-dessus d'un élément qui ne l'est pas,
     quel que soit l'ordre du document. Le formulaire passait donc sous la
     vidéo. Il ne s'en voyait rien tant que la carte portait le flou hérité de
     « .card » : un « backdrop-filter » crée un contexte d'empilement, et c'est
     lui qui la hissait — par accident. Retirer le flou a retiré l'accident.
     La position est ici déclarée pour ce qu'elle est. */
  position: relative; z-index: 3;
}

/* Le logo s'aligne sur le bord gauche du bloc, comme le reste du formulaire :
   une seule verticale pour la marque, le titre, les libellés et le bouton. */
.gate-marque { display: block; margin: 0 0 18px; width: 190px; }
.gate-card .brand-logo { margin: 0; max-width: 190px; }
.gate-card h1 { margin-bottom: 2px; }
.gate-card h1 { color: #fff; }
.gate-card .sub, .gate-card p.sub { color: rgba(255, 255, 255, .82); }
.gate-card .brand-logo { filter: invert(1); opacity: .92; }
/* Un cran plus lisible que sur la page vitrine : là un libellé accompagne
   une image, ici il désigne un champ qu'on doit remplir sans hésiter. */
.gate-card label { color: rgba(255, 255, 255, .78); }

/* Un filet sous le champ, pas une boîte : c'est ce qui laisse l'image
   respirer derrière la saisie. */
.gate-card input[type=text], .gate-card input[type=email],
.gate-card input[type=password], .gate-card input {
  background: none; border: 0; border-bottom: 1px solid rgba(255, 255, 255, .44);
  border-radius: 0; padding: 6px 0; color: #fff; box-shadow: none;
}
.gate-card input::placeholder { color: rgba(255, 255, 255, .32); }
.gate-card input:focus { outline: none; border-bottom-color: #fff; box-shadow: none; }
/* Le remplissage automatique de Chrome repeint le fond en jaune et rend le
   texte illisible sur l'image. La transition interminable est l'échappatoire
   connue : elle ne se termine jamais, donc la couleur ne s'applique jamais. */
.gate-card input:-webkit-autofill {
  -webkit-text-fill-color: #fff;
  transition: background-color 9999s;
}

.gate-card button[type=submit] {
  background: none; border: 0; padding: 0; margin-top: 26px;
  color: #fff; font-size: 1.3rem; font-weight: 500; letter-spacing: -0.012em;
  text-underline-offset: 0.22em; box-shadow: none; width: auto;
}
.gate-card button[type=submit]:hover,
.gate-card button[type=submit]:focus-visible {
  text-decoration: underline; color: #fff; background: none;
}
.gate-card .secondary { color: rgba(255, 255, 255, .68); }
.gate-card .error, .gate-card .field-error { text-shadow: none; }

/* Exception assumée : l'enrôlement du second facteur garde sa surface. Un QR
   code sur une image en mouvement ne se scanne pas, et la clé de secours se
   recopie caractère par caractère. Le fond reste, la carte redevient opaque. */
.gate-panel {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; box-shadow: var(--lift-3);
  color: var(--text); text-shadow: none;
}
.gate-panel h1, .gate-panel .sub, .gate-panel label { color: inherit; }
.gate-panel .sub, .gate-panel label { color: var(--muted); }

/* --- Direct ---------------------------------------------------------------
   La page du spectateur reprend celle du screener : même coque, même
   filigrane. Ne s'y ajoute que ce qui n'existe pas dans une vidéo déposée —
   savoir si l'antenne est ouverte, et pouvoir mettre le son. */
.live-pastille {
  display: inline-block; width: 8px; height: 8px; margin-right: 7px;
  border-radius: 50%; background: #40be82; vertical-align: 1px;
  box-shadow: 0 0 0 3px rgba(64, 190, 130, .18);
}
/* Éteinte, elle cesse aussi de battre : un point qui pulse devant une antenne
   fermée laisse croire que quelque chose arrive. */
.live-pastille:not([data-off]) { animation: live-battement 2s ease-in-out infinite; }
.live-pastille[data-off] {
  background: var(--muted); box-shadow: none; animation: none;
}
@keyframes live-battement {
  0%, 100% { box-shadow: 0 0 0 3px rgba(64, 190, 130, .18); }
  50% { box-shadow: 0 0 0 6px rgba(64, 190, 130, .06); }
}
@media (prefers-reduced-motion: reduce) {
  .live-pastille { animation: none !important; }
}

.live-attente {
  position: absolute; inset: 0; display: grid; place-items: center;
  text-align: center; padding: 24px; background: rgba(5, 5, 11, .82);
}

/* La vidéo du direct occupe le cadre au lieu de s'afficher à sa taille
   native, qui le débordait en emportant le filigrane hors de l'image.

   « contain » et non « cover » : un format scope dans un cadre 16/9 se pose
   avec des bandes, il ne se recadre pas. Rogner l'image d'un étalonnage pour
   qu'elle remplisse un rectangle serait exactement la faute qu'on ne peut pas
   commettre ici. */
.watch-player video {
  display: block; width: 100%; height: 100%; object-fit: contain; background: #000;
}

/* Deux boutons discrets dans un coin de l'image. Discrets parce qu'ils se
   posent sur un étalonnage : tout ce qui brille à côté de l'image fausse le
   jugement de celui qui la regarde. */
.live-outils {
  position: absolute; right: 12px; bottom: 12px; z-index: 4;
  display: flex; gap: 8px;
}
.live-bouton {
  display: grid; place-items: center; width: 32px; height: 32px;
  padding: 0; border: 0; border-radius: 8px; cursor: pointer;
  background: rgba(8, 8, 10, .58); color: rgba(255, 255, 255, .82);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: background .15s ease, color .15s ease, opacity .2s ease;
  opacity: .5;
}
.live-bouton:hover, .live-bouton:focus-visible {
  background: rgba(8, 8, 10, .78); color: #fff; opacity: 1;
}
/* En plein écran les commandes remontent : au ras du bord, elles tombent sous
   la barre que le système affiche au moindre mouvement de souris. */
#player:fullscreen .live-outils { right: 22px; bottom: 26px; }

.live-avertissement {
  margin: 18px 0 0; color: var(--muted); font-size: 13px; line-height: 1.65;
}

/* Une valeur à recopier dans un autre logiciel : le bouton se tient à côté
   d'elle, pas au bout de la ligne, pour qu'on voie ce qu'on copie. */
.a-copier { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.a-copier code { word-break: break-all; }
.btn.small { padding: 4px 10px; font-size: 12px; }

/* Marque de l'instance de développement. Elle ne s'affiche que lorsque
   « DEBUG » est actif, donc jamais en production — et elle doit se voir sans
   jamais gêner : un bandeau qui recouvrirait un bouton finirait masqué, et ne
   servirait plus à rien. */
.bandeau-essai {
  position: fixed; top: 0; left: 0; right: 0; z-index: 9999;
  padding: 3px 12px; text-align: center; pointer-events: none;
  background: rgba(214, 138, 42, .92); color: #17110a;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
}
body { padding-top: 0; }

/* --- Dossier de festival, côté public ------------------------------------
   Le contributeur n'est pas un utilisateur de l'outil : c'est un réalisateur
   ou un producteur à qui l'on demande un service, souvent tard et entre deux
   choses. La page se lit d'un regard et ne pose aucune question. */
.page-dossier .watch-shell { width: min(720px, 92vw); }
.dossier h1 { font-size: 1.75rem; line-height: 1.15; letter-spacing: -0.02em; margin: 0 0 6px; }
.dossier-sous { margin: 0 0 22px; color: var(--muted); font-size: 14px; }
.dossier-aide { margin: 0 0 26px; color: var(--muted); font-size: 14px; line-height: 1.65; }
.dossier-note { margin: 26px 0 0; color: var(--muted); font-size: 12.5px; line-height: 1.6; }

.dossier-avance { display: flex; align-items: center; gap: 12px; margin: 0 0 22px; }
.dossier-barre {
  flex: 1; height: 4px; border-radius: 2px; overflow: hidden;
  background: rgba(255, 255, 255, .10);
}
.dossier-barre div { height: 100%; background: var(--accent); transition: width .4s ease; }

.dossier-liste { list-style: none; padding: 0; margin: 0; display: grid; gap: 12px; }
.dossier-item {
  padding: 16px 18px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line);
}
.dossier-entete { display: flex; gap: 12px; align-items: flex-start; }
.dossier-titre { margin: 0; font-size: 15px; font-weight: 500; }
.dossier-titre .meta { font-weight: 400; margin-left: 6px; }

/* Une pastille plutôt qu'une case : rien n'est à cocher ici, l'état se
   constate. Vide tant que l'élément manque, pleine quand il est fourni. */
.dossier-etat {
  flex: none; width: 9px; height: 9px; margin-top: 6px; border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, .28);
}
.dossier-item.fait .dossier-etat { background: #40be82; box-shadow: none; }
.dossier-item.fait .dossier-titre { color: var(--muted); }

.dossier-reponse {
  width: 100%; margin-top: 12px; padding: 10px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
  font: inherit; font-size: 14px; resize: vertical;
}
.dossier-sauve { color: #40be82; margin-top: 6px; }

.dossier-fichiers { display: grid; gap: 6px; margin: 12px 0 0; }
.dossier-piece {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 8px 12px; border-radius: 8px; font-size: 13px;
  background: rgba(255, 255, 255, .04); text-decoration: none;
}
.dossier-piece:hover { background: rgba(255, 255, 255, .08); }
.dossier-item [data-ajouter] { margin-top: 12px; }
.dossier-progres {
  height: 3px; margin-top: 10px; border-radius: 2px; overflow: hidden;
  background: rgba(255, 255, 255, .10);
}
.dossier-progres div { height: 100%; width: 0; background: var(--accent); transition: width .2s ease; }

/* --- La vitrine : un dossier de presse ------------------------------------
   Ce dossier passe devant un comité qui en reçoit des centaines. Chaque
   élément est donc rendu selon ce qu'il porte — une affiche s'affiche, une
   bande-annonce se joue, un synopsis se lit — plutôt que réduit à une ligne de
   fichier. Une liste uniforme ne présente rien : elle range. */
.page-dossier.vitrine .watch-shell { width: min(980px, 92vw); }
.presse { color: var(--text); }

/* L'affiche coiffe la page, et le titre se pose à côté d'elle plutôt que
   par-dessus : une affiche est une image composée, la barrer d'un titre
   reviendrait à corriger le travail de quelqu'un. */
.presse-une {
  display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 4fr);
  gap: clamp(1.6rem, 4vw, 3rem); align-items: center; margin: 0 0 clamp(2.4rem, 6vw, 4rem);
}
.presse-une-image img {
  display: block; width: 100%; height: auto; border-radius: 10px;
  box-shadow: var(--lift-3);
}
.presse-une h1, .presse-entete h1 {
  font-size: clamp(2rem, 5vw, 3.2rem); line-height: 1.05; letter-spacing: -0.025em;
  margin: 0 0 6px;
}
.presse-vo { margin: 0 0 14px; color: var(--muted); font-size: 1.05rem; font-style: italic; }
.presse-festival {
  margin: 0; font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.presse-entete { margin: 0 0 clamp(2rem, 5vw, 3rem); }

@media (max-width: 720px) {
  .presse-une { grid-template-columns: 1fr; }
}

.presse-bloc { margin: 0 0 clamp(2rem, 5vw, 3.4rem); }
.presse-bloc h2, .presse-mentions h2 {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .74rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); font-weight: 500; margin: 0 0 14px;
  padding-bottom: 8px; border-bottom: 1px solid var(--line);
}

/* Une mesure courte pour la prose : un synopsis qui court sur mille pixels ne
   se lit pas, il se parcourt. */
.presse-prose { max-width: 62ch; font-size: 1.0625rem; line-height: 1.7; }
.presse-prose p { margin: 0 0 .9em; }
.presse-prose p:last-child { margin-bottom: 0; }

.presse-galerie {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px;
}
.presse-galerie img, .presse-figures img {
  display: block; width: 100%; height: 100%; object-fit: cover; border-radius: 8px;
  transition: opacity .2s ease;
}
.presse-galerie a { display: block; aspect-ratio: 16 / 10; overflow: hidden; border-radius: 8px; }
.presse-galerie a:hover img { opacity: .82; }

/* Une image isolée n'est pas une vignette : elle garde ses proportions. */
.presse-figures { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; }
.presse-figures a { display: block; max-width: min(320px, 100%); }
.presse-figures img { height: auto; box-shadow: var(--lift-2); }

.presse-bloc video {
  display: block; width: 100%; border-radius: 10px; background: #000;
  box-shadow: var(--lift-3);
}

.presse-documents { list-style: none; padding: 0; margin: 14px 0 0; display: grid; gap: 6px; }
.presse-documents a {
  display: flex; justify-content: space-between; gap: 16px; align-items: baseline;
  padding: 11px 14px; border-radius: 8px; text-decoration: none;
  background: var(--tuile);
}
.presse-documents a:hover { background: var(--tuile-survol); }

.presse-pied {
  margin-top: clamp(2.4rem, 6vw, 4rem); padding-top: 18px; border-top: 1px solid var(--line);
  color: var(--muted); font-size: 12.5px;
}

/* --- Visionneuse de photos ------------------------------------------------
   Une grande image et des pastilles dessous, comme la galerie d'icônes du
   Finder. Douze vignettes en grille laissent l'œil papillonner ; une seule
   grande image lui donne quelque chose à regarder. */
.visionneuse { outline: none; }
.visionneuse-scene { position: relative; display: flex; align-items: center; }

.visionneuse-piste {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  border-radius: 10px; background: #000; box-shadow: var(--lift-3);
  scrollbar-width: none;
}
.visionneuse-piste::-webkit-scrollbar { display: none; }
.visionneuse-piste img {
  flex: 0 0 100%; width: 100%; aspect-ratio: 16 / 9;
  object-fit: contain; scroll-snap-align: start; background: #000;
}

.visionneuse-fleche {
  position: absolute; z-index: 2; width: 38px; height: 38px;
  display: grid; place-items: center; border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(8, 8, 10, .55); color: rgba(255, 255, 255, .85);
  font-size: 22px; line-height: 1;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  opacity: .55; transition: opacity .15s ease, background .15s ease;
}
.visionneuse-fleche:hover, .visionneuse-fleche:focus-visible { opacity: 1; background: rgba(8, 8, 10, .78); }
.visionneuse-fleche[data-pas="-1"] { left: 10px; }
.visionneuse-fleche[data-pas="1"] { right: 10px; }
/* Sur un écran tactile, le doigt suffit et les flèches masquent l'image. */
@media (hover: none) { .visionneuse-fleche { display: none; } }

.visionneuse-pastilles {
  display: flex; gap: 8px; overflow-x: auto; padding: 12px 2px 2px;
  scrollbar-width: thin;
}
.visionneuse-pastilles button {
  flex: 0 0 auto; width: 84px; height: 52px; padding: 0; cursor: pointer;
  border: 0; border-radius: 6px; overflow: hidden; background: none;
  opacity: .42; transition: opacity .15s ease, box-shadow .15s ease;
}
.visionneuse-pastilles button img { width: 100%; height: 100%; object-fit: cover; display: block; }
.visionneuse-pastilles button:hover { opacity: .75; }
.visionneuse-pastilles button.active {
  opacity: 1; box-shadow: 0 0 0 2px var(--accent);
}

/* --- Le réalisateur : portrait et biographie côte à côte ------------------ */
.presse-auteur-corps {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2.2fr);
  gap: clamp(1.2rem, 3vw, 2rem); align-items: start;
}
.presse-auteur-photo img {
  display: block; width: 100%; height: auto; border-radius: 10px;
  box-shadow: var(--lift-2);
}
@media (max-width: 640px) { .presse-auteur-corps { grid-template-columns: 1fr; } }

/* Le synopsis se pose sous le titre, contre l'affiche : les séparer
   obligerait le lecteur à faire lui-même le rapprochement. */
.presse-synopsis { margin-top: 1.2rem; }

.presse-film { position: relative; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; }
.presse-film iframe { width: 100%; height: 100%; border: 0; display: block; }

/* Les mentions administratives ferment la marche, en petites colonnes : elles
   se consultent, elles ne se lisent pas. */
.presse-mentions {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: clamp(1.4rem, 3vw, 2.4rem); margin: 0 0 clamp(2rem, 5vw, 3.4rem);
}
.presse-mentions .presse-prose { font-size: .95rem; }

.presse-tout .btn { margin-top: 14px; }

/* --- Habillage d'un dossier de festival ----------------------------------
   Une image du film derrière la page. Floutée, parce qu'un plan net se dispute
   avec le texte posé dessus ; assombrie, parce que la lisibilité ne peut pas
   dépendre de la photo qu'on aura choisie. Le voile est uniforme et non
   dégradé : une tache claire au milieu d'un texte est pire qu'un fond gris. */
.dossier-fond {
  position: fixed; inset: -40px; z-index: 0; pointer-events: none;
  background-size: cover; background-position: center;
  filter: blur(38px) saturate(115%);
  transform: scale(1.06);
}
.dossier-fond-voile {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: rgba(6, 6, 12, .80);
}
.page-dossier .watch-shell { position: relative; z-index: 1; }

/* Le logo d'une production ne mène nulle part : nous ne connaissons pas son
   site, et un lien mort vaut moins qu'aucun lien. */
.marque-production { display: block; width: auto; max-width: 220px; }
.marque-production img {
  display: block; width: auto; max-width: 100%; max-height: 78px; height: auto;
  opacity: .95;
  /* Notre marque est un tracé noir qu'on inverse pour le poser sur du sombre.
     Celle d'une production arrive dans ses propres couleurs : l'inverser la
     défigurerait, et c'est le genre de détail qu'un client remarque avant
     tout le reste. */
  filter: none;
}

/* --- Poste de contrôle ----------------------------------------------------
   L'exploitant passe derrière la production. Il doit voir d'un regard ce qui
   attend son verdict, et ne rien avoir à chercher : la couleur porte l'état,
   la ligne validée s'efface sans disparaître. */
.controle { display: grid; gap: 12px; margin: 0 0 28px; }
.controle-item {
  padding: 16px 18px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line);
}
.controle-item.valide { opacity: .72; }
.controle-item header { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; }
.controle-titre { margin: 0; font-size: 15px; font-weight: 500; }
.controle-titre .badge { margin-left: 8px; }
.controle-etat { flex: none; }

.controle-texte { margin: 12px 0 0; }
.controle-texte textarea {
  width: 100%; padding: 10px 12px; border-radius: 8px; font: inherit; font-size: 14px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text); resize: vertical;
}
.controle-texte .btn { margin-top: 8px; }

.controle-pieces { list-style: none; padding: 0; margin: 12px 0 0; display: grid; gap: 6px; }
.controle-pieces li {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 12px; border-radius: 8px; background: rgba(255, 255, 255, .04); font-size: 13px;
}
.controle-pieces .meta { margin-left: auto; }
.controle-pieces form { flex: none; }
.controle-actions { margin-top: 14px; }

/* Le compte du poste de contrôle : ce qui attend une décision, en tête. */
.controle-somme {
  margin: 0 0 14px;
  color: var(--muted);
  font-size: 13px;
}
.controle-somme strong { color: var(--text); }

/* Une ligne validée s'efface : le regard doit tomber sur ce qui reste à lire,
   pas sur ce qui est réglé. Elle revient entière au survol, car « effacé » ne
   doit pas vouloir dire « illisible ». */
.controle-item.valide { opacity: 0.62; }
.controle-item.valide:hover,
.controle-item.valide:focus-within { opacity: 1; }

/* --- Une pièce jointe au poste de contrôle ------------------------------- */
.controle-pieces { list-style: none; margin: 12px 0 0; padding: 0; }

.piece {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.02);
}
.piece + .piece { margin-top: 8px; }
.piece-corps { flex: 1; min-width: 0; }
.piece-ligne { display: flex; align-items: center; gap: 8px; }
.piece-meta { margin: 4px 0 0; }
.piece-muet { opacity: 0.7; }

/* Une liste de pièces hors du poste de contrôle : les fichiers d'un projet.
   Même langage visuel que les pièces d'un dossier, puisque c'est la même chose
   qu'on regarde — un fichier qu'on veut reconnaître avant de le remettre.

   Toutes les lignes ont la même hauteur, et la vignette occupe sa place même
   quand il n'y a pas d'image : sans cela un PDF entre deux photos faisait une
   ligne deux fois plus courte, et la colonne devenait un escalier. */
.pieces { list-style: none; padding: 0; margin: 0 0 28px; display: grid; gap: 8px; }
.pieces .piece { align-items: center; }
.pieces .obj {
  display: block;
  /* Un nom de fichier n'a pas toujours d'espace où se couper. Sans cela
     « A108C008_130101_R3S6.mxf » pousse la ligne hors de sa boîte. */
  overflow-wrap: anywhere;
}
.pieces .meta { display: block; margin-top: 3px; }
/* L'action reste à droite et ne descend pas avec le texte : sur une ligne à
   deux lignes de métadonnées, elle se retrouvait au milieu de nulle part. */
.pieces form.inline { flex: none; margin-left: auto; align-self: center; }

/* Plus petites qu'au poste de contrôle : on y valide une pièce à la fois,
   ici on parcourt vingt-sept fichiers. Cinquante-six pixels suffisent à
   reconnaître une photo, et le clic l'ouvre en grand. */
.pieces .piece-vignette, .pieces .piece-creux { width: 56px; height: 56px; }

/* La place de la vignette, tenue même sans image. */
.piece-creux {
  flex: none;
  width: 56px;
  height: 56px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* La pièce ouverte en grand sur sa propre fiche.
   La hauteur est bornée : une affiche de quatre mille pixels de haut pousserait
   les empreintes et l'historique hors de l'écran, et c'est aussi pour eux qu'on
   vient sur cette page. */
.apercu-piece { margin: 0 0 24px; }
.apercu-piece img, .apercu-piece video {
  display: block;
  max-width: 100%;
  max-height: 60vh;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.35);
}
.apercu-piece audio { width: 100%; max-width: 520px; }
.apercu-piece iframe {
  width: 100%;
  height: 60vh;
  border: 1px solid var(--line);
  border-radius: 10px;
  /* Un PDF arrive sur fond transparent : sans fond blanc, le texte noir se
     lirait sur le fond sombre de l'application. */
  background: #fff;
}

/* La vignette : reconnaître une affiche d'un coup d'œil vaut mieux que lire
   son nom de fichier, qui ne dit rien de ce qu'elle montre. */
.piece-vignette {
  flex: none;
  display: block;
  width: 84px;
  height: 84px;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.35);
}
.piece-vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Le nom est un champ, et il doit se voir comme tel.
   Il était transparent au repos et son bouton caché jusqu'au clic : rien ne
   disait que ce nom s'éditait, et l'exploitant a cherché un bouton
   d'enregistrement qui existait mais restait invisible. Une bordure permanente
   annonce le champ ; le bouton se montre dès qu'on survole la ligne, dès qu'on
   entre dans le champ, et surtout dès que le nom a changé. */
.piece-nom { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
.piece-nom input {
  flex: 1;
  min-width: 0;
  padding: 4px 6px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: inherit;
}
.piece-nom input:hover { border-color: var(--muted); }
.piece-nom input:focus {
  outline: none;
  border-color: var(--accent);
  background: rgba(0, 0, 0, 0.25);
}
/* Réservé par « visibility » et non par « display » : le bouton garde sa place,
   sans quoi la ligne se réagencerait sous le curseur au moment de le viser. */
.piece-renommer { visibility: hidden; }
.piece:hover .piece-renommer,
.piece-nom:focus-within .piece-renommer { visibility: visible; }
/* Un nom modifié qu'on quitterait sans enregistrer serait perdu en silence :
   passé ce point le bouton ne se cache plus, et il se voit. */
.piece-nom.modifie .piece-renommer {
  visibility: visible;
  border-color: var(--accent);
  color: var(--accent);
}

.piece-video { width: 100%; max-width: 460px; margin-top: 8px; border-radius: 8px; }

.piece-lecture { margin-top: 8px; }
.piece-lecture summary { cursor: pointer; color: var(--muted); font-size: 13px; }
.piece-lecture[open] summary { margin-bottom: 8px; }
.piece-lecture iframe {
  width: 100%;
  height: 460px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
}

/* Le lecteur d'une plateforme, au poste de contrôle : on regarde ce que le
   comité regardera, dans la page où l'on valide. */
.controle-embed { margin-top: 10px; max-width: 520px; }
.controle-lu { white-space: pre-wrap; margin: 8px 0 0; }

/* ==========================================================================
   Thèmes de la page publique
   Le dossier part sous le nom du film, pas sous le nôtre : une comédie
   algérienne et un documentaire animalier n'ont aucune raison de se présenter
   dans la même couleur. Le thème ne touche que les pages remises à un tiers —
   l'outil interne garde le sien, sans quoi l'exploitant changerait d'écran
   chaque fois qu'il ouvre un dossier.

   Porté par « html » : la trame de points est dessinée par « html::before »,
   qui n'est pas un descendant de « body » et n'hériterait pas de ses
   variables.
   ========================================================================== */

/* --- Charcoal : neutre, comme une salle de projection ------------------- */
html.theme-charcoal {
  --bg: #0e0e11; --surface: rgba(26, 26, 31, .76); --raised: #1c1c22;
  --line: rgba(255, 255, 255, .11);
  --text: #ececed; --muted: #9a9aa3; --accent: #5a6076; --warm: #c9995a;
  --tuile: rgba(255, 255, 255, .05);
  --tuile-survol: rgba(255, 255, 255, .10);
  --scrim: rgba(0, 0, 0, .60);
}
/* Presque pas de lueur : un noir de salle n'est pas coloré. Deux voiles très
   faibles suffisent à ce que le fond ne soit pas un aplat mort. */
html.theme-charcoal body::before {
  background:
    radial-gradient(60rem 40rem at 8% 12%, rgba(255, 255, 255, .045), transparent 68%),
    radial-gradient(48rem 34rem at 92% 96%, rgba(255, 255, 255, .028), transparent 66%);
}
html.theme-charcoal::before {
  background-image:
    radial-gradient(circle at center, rgba(255, 255, 255, .13) 1px, transparent 1.4px);
}
/* La barre latérale est structurelle : elle suit le thème, sinon elle garde
   le bleu de minuit au milieu d'un gris de salle. */
html.theme-charcoal { --chrome: rgba(14, 14, 18, .72); --chrome-edge: rgba(255, 255, 255, .07); }
html.theme-charcoal .dossier-fond-voile { background: rgba(6, 6, 8, .84); }

/* --- Steel : la salle machine ------------------------------------------
   Presque sans teinte, et trame en lignes plutôt qu'en points. Les lueurs
   bleues et mauves de minuit habillent bien une page qu'on montre ; devant un
   écran d'étalonnage, toute couleur dans l'interface est une couleur de trop,
   parce qu'elle entre en concurrence avec l'image qu'on juge.

   Le gris reste légèrement froid et non parfaitement neutre : un gris sans
   aucune teinte paraît verdâtre à côté d'une image étalonnée chaude, et l'œil
   le corrige tout seul en déplaçant sa référence. */
html.theme-steel {
  --bg: #0f1216; --surface: rgba(22, 27, 33, .78); --raised: #171c22;
  --line: rgba(255, 255, 255, .10);
  --text: #e6e9ed; --muted: #8d969f; --accent: #4ea3c4; --warm: #bf8a4e;
  --error: #e06c6c; --ok: #4ab59a;
  --chrome: rgba(12, 15, 19, .76); --chrome-edge: rgba(255, 255, 255, .06);
  --tuile: rgba(255, 255, 255, .045);
  --tuile-survol: rgba(255, 255, 255, .09);
  --scrim: rgba(4, 6, 8, .62);

  /* Des ombres franches et courtes : une salle machine n'a pas de halo. */
  --lift-1: 0 1px 2px rgba(0, 0, 0, .40);
  --lift-2: 0 2px 6px rgba(0, 0, 0, .44), 0 10px 28px rgba(0, 0, 0, .34);
  --lift-3: 0 6px 18px rgba(0, 0, 0, .48), 0 26px 60px rgba(0, 0, 0, .40);
}
/* Deux voiles à peine, et froids. Il ne s'agit pas d'éclairer la page mais
   d'éviter l'aplat mort, qui se casse en bandes sur un écran huit bits. */
html.theme-steel body::before {
  background:
    radial-gradient(64rem 44rem at 4% 14%, rgba(120, 160, 190, .070), transparent 68%),
    radial-gradient(50rem 34rem at 94% 94%, rgba(120, 150, 180, .045), transparent 66%);
}
/* La trame passe du point à la ligne, et cesse d'être masquée : un quadrillage
   régulier se lit comme une surface technique, là où des points dispersés
   sous une lueur se lisent comme une matière. */
html.theme-steel::before {
  background-image:
    linear-gradient(to right, rgba(170, 195, 215, .052) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(170, 195, 215, .052) 1px, transparent 1px);
  background-size: 34px 34px, 34px 34px;
  -webkit-mask-image: none;
  mask-image: none;
}
/* Le grain s'allège : il sert à casser un dégradé, et il n'y a presque plus
   de dégradé à casser. */
html.theme-steel body::after { opacity: .20; }

/* --- Paper : clair, comme un dossier de presse imprimé ------------------ */
html.theme-paper {
  --bg: #f3f1ec; --surface: rgba(255, 255, 255, .88); --raised: #ffffff;
  --line: rgba(24, 22, 48, .14);
  --text: #191826; --muted: #5c5a6e; --accent: #4a48c4; --warm: #a86a1f;
  --error: #b3261e; --ok: #1c7a5b;
  --tuile: rgba(24, 22, 48, .045);
  --tuile-survol: rgba(24, 22, 48, .085);
  --scrim: rgba(255, 255, 255, .66);

  /* Les ombres d'un thème clair sont grises et courtes. Reprendre celles du
     thème sombre poserait des taches noires sur du papier. */
  --lift-1: 0 1px 2px rgba(24, 22, 48, .10);
  --lift-2: 0 2px 6px rgba(24, 22, 48, .10), 0 10px 30px rgba(24, 22, 48, .07);
  --lift-3: 0 6px 18px rgba(24, 22, 48, .12), 0 28px 64px rgba(24, 22, 48, .10);
}
/* Sur du papier, la barre devient claire elle aussi. Elle gardait le bleu
   de minuit, et le texte du thème, devenu sombre, s'y effaçait : la
   navigation entière était illisible. */
html.theme-paper { --chrome: rgba(255, 255, 255, .82); --chrome-edge: rgba(24, 22, 48, .08); }
/* Notre marque est un tracé noir qu'on inverse pour la poser sur du sombre :
   sur une barre claire, il faut la laisser noire. */
html.theme-paper .brand-logo, html.theme-paper .brand-mini img { filter: none; }

html.theme-paper body::before {
  background:
    radial-gradient(62rem 42rem at 2% 20%, rgba(120, 140, 235, .14), transparent 66%),
    radial-gradient(52rem 36rem at 96% 92%, rgba(226, 168, 96, .12), transparent 68%);
}
/* Les points s'assombrissent au lieu de s'éclaircir, et s'allègent : la même
   densité qui accroche la lumière sur du noir salit du papier. */
html.theme-paper::before {
  background-image:
    radial-gradient(circle at center, rgba(24, 22, 48, .13) 1px, transparent 1.4px);
}
/* Le grain est fait pour du sombre : en « overlay » sur du clair il vire au
   sale. En « multiply » très léger, il reste une texture de papier. */
html.theme-paper body::after { opacity: .16; mix-blend-mode: multiply; }

/* Notre marque est un tracé noir qu'on inverse pour la poser sur du sombre.
   Sur du papier, l'inverser la rendrait blanche sur blanc — donc invisible. */
html.theme-paper .watch-brand img,
html.theme-paper .galerie-marque { filter: none; }

/* Le voile qui passe derrière le texte suit le fond, sinon une image de film
   assombrirait la page sous un texte noir. */
html.theme-paper .dossier-fond-voile { background: rgba(246, 244, 239, .88); }
html.theme-paper .dossier-barre { background: rgba(24, 22, 48, .12); }

/* Ces deux-là sont posés sur des images et restent sombres dans tous les
   thèmes : un galet clair sur une photo claire disparaît. */
html.theme-paper .visionneuse-cadre,
html.theme-paper .presse-film { background: #000; }

/* --- Choisir l'habillage d'un dossier ------------------------------------
   Une image se choisit à l'œil. « logo_final_v3.png » dans une liste
   déroulante ne dit rien de ce qu'il montre, et l'on découvrait son erreur
   sur la page du festival. */

.habillage-themes { display: flex; flex-wrap: wrap; gap: 10px; }
.habillage-theme { cursor: pointer; }
.habillage-theme input { position: absolute; opacity: 0; pointer-events: none; }

/* La pastille montre les trois teintes qui décident de tout : le fond, le
   texte, l'accent. Trois carrés valent mieux qu'un nom de couleur. */
.habillage-pastille {
  display: flex; width: 92px; height: 58px; overflow: hidden;
  border: 1px solid var(--line); border-radius: 8px;
  transition: border-color var(--tempo) var(--courbe), box-shadow var(--tempo) var(--courbe);
}
.habillage-pastille-fond { flex: 3; }
.habillage-pastille-texte { flex: 1; }
.habillage-pastille-accent { flex: 1; }

.habillage-theme.theme-midnight .habillage-pastille-fond { background: #14133a; }
.habillage-theme.theme-midnight .habillage-pastille-texte { background: #eeecfb; }
.habillage-theme.theme-midnight .habillage-pastille-accent { background: #7d7bf5; }
.habillage-theme.theme-charcoal .habillage-pastille-fond { background: #0e0e11; }
.habillage-theme.theme-charcoal .habillage-pastille-texte { background: #ececed; }
.habillage-theme.theme-charcoal .habillage-pastille-accent { background: #5a6076; }
.habillage-theme.theme-paper .habillage-pastille-fond { background: #f3f1ec; }
.habillage-theme.theme-paper .habillage-pastille-texte { background: #191826; }
.habillage-theme.theme-paper .habillage-pastille-accent { background: #4a48c4; }
.habillage-theme.theme-steel .habillage-pastille-fond { background: #0f1216; }
.habillage-theme.theme-steel .habillage-pastille-texte { background: #e6e9ed; }
.habillage-theme.theme-steel .habillage-pastille-accent { background: #4ea3c4; }

.habillage-theme input:checked + .habillage-pastille {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}
.habillage-theme input:focus-visible + .habillage-pastille { outline: 2px solid var(--accent); outline-offset: 3px; }
.habillage-theme .habillage-nom { display: block; max-width: 92px; text-align: center; }

.habillage-choix {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 10px;
}
.habillage-vignette, .habillage-depot { cursor: pointer; min-width: 0; }
.habillage-vignette input, .habillage-depot input {
  position: absolute; opacity: 0; pointer-events: none;
}
.habillage-cadre {
  display: grid; place-items: center;
  aspect-ratio: 4 / 3; overflow: hidden;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--tuile); color: var(--muted); font-size: 12.5px; text-align: center;
  transition: border-color var(--tempo) var(--courbe), box-shadow var(--tempo) var(--courbe);
}
.habillage-cadre img { width: 100%; height: 100%; object-fit: contain; display: block; }
.habillage-vignette input:checked + .habillage-cadre {
  border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent);
}
.habillage-vignette input:focus-visible + .habillage-cadre { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Le nom reste sous la vignette, tronqué : il sert à lever un doute entre
   deux images voisines, pas à être lu en entier. */
.habillage-nom {
  display: block; margin-top: 4px; font-size: 11.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.habillage-depot .habillage-cadre {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.habillage-depot.survol .habillage-cadre {
  border-style: solid; border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.habillage-depot-mot { padding: 6px; line-height: 1.35; }
.habillage-etat.erreur { color: var(--error); white-space: normal; }

/* ==========================================================================
   Le portail sur un téléphone
   ==========================================================================

   Le défaut qu'on voyait : en partant sur le côté, le fond à pointillés
   s'arrêtait net et la page continuait. La cause n'était pas le fond mais la
   page — un tableau de six colonnes rendait le document plus large que
   l'écran (601 px mesurés sur un écran de 375). On pouvait donc défiler
   au-delà des calques décoratifs, qui eux sont fixes et ne couvrent que la
   fenêtre. On corrige les débordements un par un, puis on pose un filet.
   ========================================================================== */

/* --- 1. Ce qui débordait ------------------------------------------------- */

/* Un tableau de données ne se plie pas : ses colonnes ont une largeur
   minimale, et un écran de téléphone n'en contient pas six. Plutôt que de le
   comprimer jusqu'à l'illisible, on lui donne son propre défilement
   horizontal, à l'intérieur de la page.

   « overscroll-behavior-x: contain » n'est pas un détail de confort : sans
   lui, un glissé vers la droite arrivé au bout du tableau déclenche le retour
   arrière du navigateur, et l'on perd la page. */
.table-defile {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  border-radius: 12px;
  position: relative;
}
/* Le tableau prend sa largeur naturelle et le conteneur défile. Le laisser se
   comprimer donnait des colonnes de trois caractères où chaque titre tombait
   sur quatre lignes : la page tenait dans l'écran, mais ne se lisait plus. */
.table-defile > table.data { width: max-content; min-width: 100%; }

@media (max-width: 760px) {
  /* La première colonne reste en place : sans elle, on fait défiler des
     chiffres sans savoir de quelle ligne ils sont. Fond opaque, car une
     surface translucide laisserait passer le texte qui glisse dessous. */
  .table-defile th:first-child,
  .table-defile td:first-child {
    position: sticky; left: 0; z-index: 1;
    /* Le fond n'apparaît qu'une fois le tableau déplacé : au repos, une
       colonne teintée se lirait comme une colonne mise en avant, ce qu'elle
       n'est pas. */
    transition: background-color 160ms var(--courbe);
  }
  .table-defile.reste-a-gauche th:first-child,
  .table-defile.reste-a-gauche td:first-child { background: var(--raised); }
  .table-defile thead th:first-child { z-index: 2; }

  /* Deux ombres portées par le conteneur, allumées par le script selon qu'il
     reste du contenu d'un côté ou de l'autre. Une ombre dit « ça continue » ;
     une barre de défilement, sur un téléphone, ne dit rien du tout. */
  .table-defile::before, .table-defile::after {
    content: ""; position: absolute; top: 0; bottom: 0; width: 22px;
    pointer-events: none; opacity: 0; transition: opacity 160ms var(--courbe);
    z-index: 3;
  }
  .table-defile::before {
    left: 0; background: linear-gradient(to right, rgba(0,0,0,.45), transparent);
  }
  .table-defile::after {
    right: 0; background: linear-gradient(to left, rgba(0,0,0,.45), transparent);
  }
  .table-defile.reste-a-gauche::before { opacity: 1; }
  .table-defile.reste-a-droite::after { opacity: 1; }
}

/* Une grille dont la colonne vaut « 1fr » a pour minimum la largeur minimale
   de son contenu : un seul élément large — ici un aperçu de document — et la
   colonne entière déborde. « minmax(0, 1fr) » autorise la colonne à devenir
   plus étroite que ce qu'elle contient, ce qui est précisément ce qu'on veut
   quand ce qu'elle contient sait se réduire. */
.controle { grid-template-columns: minmax(0, 1fr); }

/* Un « iframe » sans dimension déclarée pèse 300 px de large dans le calcul
   des minimums, même quand sa règle dit « 100 % ». Il gonflait la ligne, qui
   gonflait la grille, qui débordait la page. */
.piece-lecture, .piece-lecture iframe, .piece-ligne, .piece-corps { min-width: 0; }

/* Un champ de texte a une largeur naturelle d'une vingtaine de caractères
   qu'il ne descend pas en dessous, à moins qu'on l'y autorise. */
.piece-nom, .piece-nom input { min-width: 0; }

/* Un nom de DCP ou une empreinte n'ont pas d'espace où se couper. Sans cette
   règle, un seul de ces mots impose sa longueur à toute la page. */
.obj, .mono, code, .crumb, .page-head h1, .sub, td, dd {
  overflow-wrap: anywhere;
}

@media (max-width: 760px) {
  /* Sur un téléphone, une pièce jointe se lit en deux temps : la vignette et
     son nom, puis ce qu'on peut en faire. Les tenir sur une seule ligne
     donnait un bouton « Remove » à quarante pixels du bord. */
  .piece { flex-wrap: wrap; }
  .piece > form { margin-left: auto; }
  /* Sauf la liste d'un projet : elle n'a pas de champ de saisie, seulement un
     nom qui sait se couper. Repliée, elle envoyait « Remove » seul sur une
     deuxième ligne, à quoi plus rien ne le rattachait, et les lignes n'avaient
     plus deux fois la même hauteur. */
  .pieces .piece { flex-wrap: nowrap; }
  .pieces .piece-vignette, .pieces .piece-creux { width: 44px; height: 44px; }
  .pieces .piece-creux { font-size: 10px; }
  .piece-lecture iframe { height: 320px; }
  .apercu-piece iframe { height: 420px; }
  .apercu-piece img, .apercu-piece video { max-height: 48vh; }
  /* La vignette rapetisse et le corps garde le droit de passer à la ligne :
     un champ de texte ne descend pas sous sa largeur naturelle d'une
     vingtaine de caractères, et c'est lui qui poussait la page. */
  .piece-vignette { width: 64px; height: 64px; }
  .piece-corps { flex: 1 1 150px; }
}

/* --- 2. Le filet --------------------------------------------------------- */

/* Les débordements ci-dessus sont corrigés à la source ; celui-ci est un
   filet pour ceux qu'on écrira demain. « clip » et non « hidden » : « hidden »
   ferait de la racine un conteneur de défilement, ce qui casserait « position:
   sticky » partout dans l'application. Ici, rien ne défile latéralement, donc
   on ne peut plus sortir de la page — et le fond ne se détache plus. */
html { overflow-x: clip; }

/* Et les calques décoratifs débordent volontairement de la fenêtre : au
   rebond d'un défilement élastique, ou sous un pincement, on ne découvre plus
   leur arête. */
body::before, body::after, html::before { inset: -12vmax; }

/* --- 3. Sous l'encoche --------------------------------------------------- */

/* La page est servie en « viewport-fit=cover » : elle occupe l'écran entier,
   encoche et barre de gestes comprises. À charge pour nous d'écarter le
   contenu de ces zones, sinon un bouton se retrouve sous la barre d'accueil
   et ne répond plus. */
@media (max-width: 760px) {
  .barre-mobile {
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
    padding-top: max(10px, env(safe-area-inset-top));
  }
  .page {
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
    padding-bottom: max(56px, calc(24px + env(safe-area-inset-bottom)));
  }
  .sidebar {
    padding-left: max(12px, env(safe-area-inset-left));
    padding-top: max(18px, env(safe-area-inset-top));
    padding-bottom: max(24px, env(safe-area-inset-bottom));
  }
  .watch-shell { padding-bottom: max(32px, calc(16px + env(safe-area-inset-bottom))); }
}

/* --- 4. Le toucher ------------------------------------------------------- */

/* Le rectangle gris que pose le navigateur au moindre appui arrive après le
   doigt et reste après lui : il dit « quelque chose s'est passé » un instant
   trop tard. On l'enlève, et l'on répond nous-mêmes, à l'appui. */
@media (hover: none) {
  a, button, summary, label, [role="button"] { -webkit-tap-highlight-color: transparent; }

  /* Une réponse à l'enfoncement, jamais au relâchement : attendre le clic
     donne une interface morte. Très court, et sans rebond — ce n'est pas un
     geste porteur d'élan, c'est un accusé de réception. */
  .btn:active, .nav a:active, .tile:active, .results li a:active,
  .attention-item a:active, .habillage-vignette:active .habillage-cadre {
    transform: scale(.985);
    transition: transform 90ms var(--courbe);
  }

  /* Une cible tactile se vise avec un pouce, pas avec un curseur. */
  .nav a { padding-top: 11px; padding-bottom: 11px; }
  .sidebar-foot .quit { padding: 8px 0; }
}

/* La barre du haut flotte au-dessus du contenu qui passe dessous : un trait
   d'un pixel marque une frontière, un dégradé marque une profondeur. */
@media (max-width: 760px) {
  .barre-mobile { border-bottom: 0; }
  .barre-mobile::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -14px; height: 14px;
    pointer-events: none;
    background: linear-gradient(to bottom, var(--chrome), transparent);
  }
}

/* --- 5. Le tiroir -------------------------------------------------------- */

/* Le tiroir suit le doigt : sa position est écrite par le script, image par
   image. La transition CSS est donc retirée pendant le geste — deux moteurs
   d'animation sur la même propriété se disputent, et le tiroir tremble. */
.sidebar.au-doigt { transition: none; }

@media (max-width: 760px) {
  .sidebar { overscroll-behavior: contain; will-change: transform; }
  /* Le voile s'épaissit à mesure que le tiroir sort : on voit combien il en
     reste à tirer, et le geste devient réversible sans surprise. */
  .voile { opacity: 0; transition: opacity 220ms var(--courbe); }
  .voile.visible { opacity: 1; }

  /* Une bande de quelques millimètres au bord gauche, où l'on peut attraper
     le tiroir sans viser le bouton. Invisible, et sans effet sur le reste. */
  .prise-bord {
    position: fixed; left: 0; top: 0; bottom: 0; width: 20px; z-index: 34;
    touch-action: pan-y;
  }
}

/* Le corps ne défile plus derrière le tiroir. « overflow: hidden » ne suffit
   pas sur iOS, qui continue de défiler et perd la place à la fermeture ; on
   fige donc le corps à sa position, que le script restitue ensuite. */
body.fige { position: fixed; left: 0; right: 0; overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .sidebar, .voile { transition: none; }
}

/* --- 6. Les champs, partout ---------------------------------------------

   Écrit avec « :where » : spécificité nulle, donc chaque règle existante
   continue de l'emporter. C'est un fond de garantie, pas une reprise en main —
   un champ posé hors d'un « .form » restait blanc, avec la bordure du
   navigateur, au milieu d'une interface sombre. */
:where(input[type="text"], input[type="email"], input[type="url"],
       input[type="number"], input[type="date"], input[type="search"],
       input[type="password"], input[type="tel"], select, textarea) {
  font: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 9px 12px;
  max-width: 100%;
}

@media (max-width: 760px) {
  /* Seize pixels, et pas quinze. En dessous, iOS considère le champ trop petit
     pour être lu et zoome la page à la mise au point : tout saute, et il faut
     pincer pour revenir. C'est la principale raison pour laquelle un site
     « fait au clavier » se trahit dès la première frappe sur un téléphone. */
  input, select, textarea { font-size: 16px; }

  /* Un filtre sur une demi-largeur laisse une colonne de vide à sa droite et
     donne une cible plus étroite qu'un pouce. */
  .filters { flex-direction: column; align-items: stretch; }
  .filters .field, .filters .field.grow { width: 100%; min-width: 0; }
  .filters select, .filters input { width: 100%; }

  /* Les actions d'en-tête passent à la ligne plutôt que de se comprimer, et
     gardent une hauteur qui se vise au pouce. */
  .page-head { flex-direction: column; align-items: stretch; gap: 14px; }
  .page-head .actions { flex-wrap: wrap; }
  .page-head .actions .btn, .form-actions .btn { min-height: 44px; display: inline-flex;
    align-items: center; justify-content: center; }

  /* Un titre de page se resserre en grandissant : à cette taille, l'espacement
     par défaut fait flotter les lettres. */
  .page-head h1 { font-size: 1.5rem; letter-spacing: -0.015em; line-height: 1.15; }
}

/* --- Chez qui suis-je ? ---------------------------------------------------
   Un exploitant qui aide trois maisons dans la matinée doit le voir sans
   chercher : sans quoi il corrigera le synopsis du mauvais film. */

.espace-choix { margin: 0 0 10px; }
.espace-choix select {
  width: 100%; padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
  font: inherit; font-size: 13px;
}
/* En visite, le sélecteur porte la couleur du bandeau : c'est le seul élément
   qui reste sous les yeux quelle que soit la page, et il doit dire chez qui
   l'on se trouve sans qu'on ait à y penser. */
.espace-choix.visite select {
  border-color: var(--warm);
  box-shadow: 0 0 0 1px var(--warm) inset;
}
.espace-seul {
  margin: 0 0 10px; padding: 8px 10px; border-radius: 8px;
  background: rgba(255, 255, 255, .04); color: var(--muted); font-size: 13px;
}

/* En visite chez un client : un bandeau qui ne s'oublie pas. Ambre comme le
   bandeau d'essai, parce qu'il dit la même chose — « attention à l'endroit où
   tu te trouves ». */
.bandeau-visite {
  /* Pas « sticky » : le bandeau d'essai occupe déjà le haut de l'écran, et
     deux bandeaux collés au même bord se recouvrent. Celui-ci se lit à
     l'arrivée ; c'est le sélecteur, teinté, qui rappelle ensuite où l'on est. */
  padding: 7px 16px; text-align: center;
  background: var(--warm); color: #241a08;
  font-size: 12.5px; font-weight: 600; letter-spacing: .02em;
}
.bandeau-visite strong { font-weight: 700; }
/* Le bandeau d'essai est fixe et couvrirait celui-ci : en développement
   seulement, on lui laisse la place. */
.bandeau-visite.sous-essai { margin-top: 18px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* --- Le compteur d'un espace ---------------------------------------------
   Une jauge et non une somme : ce que le client lit doit lui dire où il en
   est, pas ce qu'il devra. Le montant, il le connaît déjà — c'est un forfait. */

.compteur { margin: 0 0 28px; }
.compteur-ligne + .compteur-ligne { margin-top: 16px; }
.compteur-tete {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; margin-bottom: 6px;
}
.compteur-nom { font-size: 13px; color: var(--muted); }
.compteur-chiffre { font-size: 14px; font-variant-numeric: tabular-nums; }

.jauge {
  height: 6px; border-radius: 3px; overflow: hidden;
  background: rgba(255, 255, 255, .10);
}
.jauge div {
  height: 100%; background: var(--accent);
  transition: width .4s var(--courbe);
}
/* Au-delà du forfait, la teinte change plutôt que la barre ne déborde : une
   barre qui sort de son cadre se lit comme un défaut d'affichage. */
.jauge div.pleine { background: var(--warm); }

/* --- Déposer un film, le voir, le partager -------------------------------
   Le geste qu'on vient faire est en tête de page, et la liste ressemble à ce
   qu'elle contient : des films, donc des images. Une ligne de tableau ne dit
   rien de ce qu'on est en train d'envoyer à un festival. */

.depot-film {
  border: 1px dashed color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 14px;
  background: var(--tuile);
  margin: 0 0 26px;
  transition: border-color var(--tempo) var(--courbe), background-color var(--tempo) var(--courbe);
}
.depot-film.survol {
  border-style: solid; border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.depot-film-corps {
  display: grid; justify-items: center; gap: 6px;
  padding: 30px 20px; cursor: pointer; text-align: center;
}
.depot-film-mot { font-size: 15px; font-weight: 500; }
.depot-film .hint { max-width: 46ch; margin: 0; }
.depot-film-etat { font-size: 13px; color: var(--muted); min-height: 1.2em; }
.depot-film-etat.erreur { color: var(--error); }
.depot-film-barre {
  display: none; width: min(420px, 80%); height: 4px; border-radius: 2px;
  background: rgba(255, 255, 255, .10); overflow: hidden;
}
.depot-film.en-cours .depot-film-barre { display: block; }
.depot-film-barre span {
  display: block; height: 100%; width: 0; background: var(--accent);
  transition: width .3s var(--courbe);
}

.films {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 18px;
}
.film {
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  background: var(--surface); box-shadow: var(--lift-1);
}
.film-vignette {
  display: grid; place-items: center;
  aspect-ratio: 16 / 9; background: #000; overflow: hidden;
}
.film-vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }
.film-attente { color: var(--muted); font-size: 13px; }
.film-corps { padding: 12px 14px 14px; }
.film-titre { margin: 0 0 2px; font-weight: 500; }
.film-titre a { color: var(--text); }
.film-corps .meta { margin: 0 0 10px; }

/* La fenêtre de partage ne quitte pas la fiche : on voit encore de quel film
   il s'agit pendant qu'on décide pour qui. */
.film-partage { margin-top: 10px; }
.film-partage[open] { padding-top: 4px; border-top: 1px solid var(--line); }
.film-partage summary { list-style: none; }
.film-partage summary::-webkit-details-marker { display: none; }
.film-partage .field { margin-bottom: 10px; }
.film-partage label { display: block; margin-bottom: 4px; font-size: 12.5px; color: var(--muted); }
.film-partage input, .film-partage select {
  width: 100%; padding: 7px 10px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text); font: inherit;
  font-size: 13.5px;
}
.film-partage .hint { font-size: 11.5px; }
.film-partage-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

/* Interrupteur. Une case à cocher demande de lire l'étiquette pour savoir ce
   qu'elle fera ; un interrupteur montre son état d'un coup d'œil, comme sur un
   téléphone. La case reste dessous : elle porte le nom, la valeur, le clavier
   et le lecteur d'écran. On ne dessine que par-dessus. */
label.bascule {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 12px; cursor: pointer;
  font-size: 14px; color: var(--text);
}
.bascule-texte { margin: 0; font-size: 14px; color: var(--text); }
label.bascule input {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.bascule-piste {
  position: relative; flex: none; width: 46px; height: 28px; border-radius: 999px;
  background: var(--raised); border: 1px solid var(--line);
  transition: background-color var(--tempo) var(--courbe),
              border-color var(--tempo) var(--courbe);
}
.bascule-pouce {
  position: absolute; top: 2px; left: 2px; width: 22px; height: 22px;
  border-radius: 50%; background: #fff; box-shadow: var(--lift-1);
  transition: transform var(--tempo) var(--courbe);
}
label.bascule input:checked + .bascule-piste {
  background: var(--accent); border-color: var(--accent);
}
label.bascule input:checked + .bascule-piste .bascule-pouce { transform: translateX(18px); }
label.bascule input:focus-visible + .bascule-piste {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* Le pouce glisse ; pour qui a demandé moins de mouvement, il apparaît. */
@media (prefers-reduced-motion: reduce) {
  .bascule-piste, .bascule-pouce { transition: none; }
}

/* Aperçu du lien : la page du destinataire, à l'échelle, dans la fiche. Le
   cadre garde le rapport d'un écran d'ordinateur pour que ce qu'on voit ici
   soit ce qui arrivera là-bas. */
.apercu-destinataire { margin-top: 12px; }
.apercu-destinataire summary { cursor: pointer; font-size: 0.8125rem; color: var(--muted); }
.apercu-destinataire summary:hover { color: var(--text); }
.apercu-cadre {
  margin-top: 10px; border: 1px solid var(--line); border-radius: 12px;
  overflow: hidden; background: var(--bg); box-shadow: var(--lift-1);
}
.apercu-cadre iframe {
  display: block; width: 100%; height: 520px; border: 0;
}
@media (max-width: 720px) {
  .apercu-cadre iframe { height: 380px; }
}

/* Affiche du film. L'image se voit à sa vraie proportion : choisir une vignette
   sans la voir revient à ne pas choisir. */
.vignette-actuelle {
  margin-bottom: 12px; border: 1px solid var(--line); border-radius: 10px;
  overflow: hidden; background: var(--bg); aspect-ratio: 16 / 9;
}
.vignette-actuelle img { display: block; width: 100%; height: 100%; object-fit: cover; }
.depot-vignette {
  border: 1px dashed var(--line); border-radius: 10px; padding: 14px;
  text-align: center; transition: border-color var(--tempo) var(--courbe),
                                  background-color var(--tempo) var(--courbe);
}
.depot-vignette.survol { border-color: var(--accent); background: rgba(125,123,245,.08); }
.depot-vignette .hint { margin: 8px 0 0; }

/* Pistes de sous-titres : une ligne, son retrait au bout. */
ul.pistes { list-style: none; margin: 0 0 14px; padding: 0; }
ul.pistes li {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--line); font-size: 14px;
}
ul.pistes li:last-child { border-bottom: 0; }
ul.pistes .meta { margin-left: 6px; text-transform: uppercase; font-size: 11.5px; }

/* Renvois vers les lignes qui attendent une relecture. Le compte seul obligeait
   à parcourir la liste entière pour retrouver les deux qu'il annonçait. */
.controle-renvois { margin: -6px 0 16px; font-size: 13px; }
.controle-renvois a { color: var(--accent); }

/* Les actions d'une pièce, empilées à droite : « Show first » n'apparaît que
   lorsqu'il y a un choix à faire, et disparaît sur celle qui mène déjà. */
.piece-actions { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
.piece-actions .badge { white-space: nowrap; }

/* Le fichier à emporter, sous le lecteur.
   La section n'avait aucune règle : le bouton touchait le bord de la vidéo —
   zéro pixel d'écart, mesuré — et sa mention courait sur toute la largeur.
   L'action principale de cette page avait l'air d'une note de bas de page.

   Le panneau reprend la matière du lecteur indisponible, qui est l'idiome de
   cette page : surface, filet, et un dégradé à peine perceptible. Le bouton et
   sa mention forment un bloc, côte à côte tant qu'il y a de la place. */
.telechargement {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 20px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, .055), rgba(255, 255, 255, .012) 46%);
}
.telechargement .btn { flex: none; }
/* « 22ch » de base pour que la mention passe à la ligne avant de devenir
   illisible, « 62ch » de plafond parce qu'au-delà l'œil perd la ligne. */
.telechargement .hint { flex: 1 1 22ch; max-width: 62ch; margin: 0; }
.telechargement .hint strong { color: var(--text); }

/* Un fichier tombé change de bord, pas de forme : on le remarque sans que la
   page se réorganise sous les yeux de celui qui la relit. */
.telechargement.tombe { border-color: rgba(232, 163, 74, .45); }

/* Le fichier prêt n'a plus de mention : le panneau n'a donc plus rien à
   encadrer. Un cadre autour d'un bouton seul est une décoration, pas une
   structure — il ne reste que l'écart au lecteur, qui lui sert à quelque
   chose. Les deux autres états gardent le leur : ils sont faits de texte, et
   un texte nu sous une vidéo se perd. */
.telechargement.pret {
  padding: 0;
  border: 0;
  background: none;
}

@media (max-width: 560px) {
  /* Au doigt, la cible occupe la largeur : viser un bouton de 153 px sur un
     téléphone tenu d'une main est un exercice, pas une action. */
  .telechargement { padding: 14px; gap: 12px; }
  .telechargement .btn { width: 100%; text-align: center; }
  .telechargement .hint { flex-basis: 100%; }
}

/* --- Politique de confidentialité ------------------------------------------
   Une page qu'on lit d'un bout à l'autre, pas un écran d'outil : une colonne
   étroite, un texte aéré, et le début du texte en haut plutôt qu'au centre. */
.page-confidentialite { align-items: flex-start; }
.page-confidentialite .watch-shell { width: min(720px, 92vw); }
.confidentialite h1 { font-size: 1.75rem; line-height: 1.15; letter-spacing: -0.02em; margin: 0 0 6px; }
.confidentialite h2 { font-size: 1.0625rem; margin: 30px 0 8px; }
.confidentialite p, .confidentialite li { line-height: 1.65; }
.confidentialite ul { padding-left: 1.2em; margin: 0 0 12px; }
.confidentialite li { margin-bottom: 6px; }
.confidentialite-date { color: var(--muted); font-size: 0.875rem; margin: 0 0 24px; }
.confidentialite a { color: inherit; }

/* --- Remise ---------------------------------------------------------------
   La page du destinataire reprend le fond de la connexion : il dit ce que fait
   la maison pendant qu'il attend son téléchargement. Ce qui change, c'est la
   hiérarchie — là on tape deux champs qu'on connaît par cœur, ici on lit des
   noms de fichiers qu'on ne connaît pas.

   D'où une règle : le titre flotte sur l'image comme sur la page de connexion,
   mais la liste pose sur une surface. Un nom de fichier long, en petites
   capitales techniques, ne se lit pas à l'ombre portée. Le fond est déjà
   flouté et voilé à 68 % par « .gate-bg::after » : la surface n'a donc qu'à
   asseoir la lecture, pas à masquer la vidéo. */
body.page-remise { background: #05050b; }

.remise-shell {
  position: relative; z-index: 2;
  display: grid; place-items: center; align-content: center;
  min-height: 100vh; min-height: 100svh;
  padding: clamp(2rem, 6vh, 4rem) clamp(1.4rem, 4vw, 3.4rem);
}

.remise-marque {
  position: relative; z-index: 3;
  display: block; width: 150px; margin: 0 auto 26px;
}
.remise-marque img { display: block; width: 100%; height: auto; filter: invert(1); opacity: .92; }

.remise-carte {
  position: relative; z-index: 3;
  width: min(34rem, 100%); color: #fff; text-align: center;
  text-shadow: 0 1px 4px rgba(0, 0, 0, .85), 0 0 18px rgba(0, 0, 0, .62);
}
.remise-carte h1 {
  margin: 0 0 6px; color: #fff;
  font-size: clamp(1.5rem, 4vw, 2.1rem); line-height: 1.15; letter-spacing: -0.02em;
}
.remise-mot { margin: 0 0 30px; color: rgba(255, 255, 255, .84); font-size: 1rem; }

/* Le bouton principal. Plein, parce que c'est le seul geste de la page : celui
   qui vient chercher un DCP ne doit pas avoir à choisir entre sept lignes. */
/* Le geste principal, dessiné comme celui de la page de dépôt. C'était un lien
   dont le texte se soulignait à l'approche du pointeur : un souvenir de la
   règle générale « a:hover », qui n'a rien à faire sur un bouton. */
.remise-tout {
  width: 100%; margin: 0 0 18px; padding: 15px 22px;
  font-size: 1.05rem; border-radius: 12px;
  justify-content: center; text-decoration: none;
}
.remise-tout:hover, .remise-tout:focus-visible { text-decoration: none; }
.remise-poids {
  display: block; margin-top: 3px;
  font-size: .82rem; font-weight: 400; color: rgba(0, 0, 0, .58);
}

.remise-liste { list-style: none; margin: 0; padding: 0; text-align: left; }
.remise-liste li + li { margin-top: 6px; }
.remise-liste button {
  display: flex; align-items: baseline; gap: 14px; width: 100%;
  padding: 12px 16px; border-radius: 10px;
  background: rgba(255, 255, 255, .07);
  border: 1px solid rgba(255, 255, 255, .11);
  color: #fff; text-align: left; font: inherit; cursor: pointer;
  text-decoration: none; text-shadow: none; box-shadow: none;
}
.remise-liste button:hover:not(:disabled),
.remise-liste button:focus-visible:not(:disabled) {
  background: rgba(255, 255, 255, .14); border-color: rgba(255, 255, 255, .24);
}
.remise-liste button:disabled { opacity: .45; cursor: not-allowed; }
/* Le nom passe à la ligne plutôt que d'être rogné : c'est la seule chose qui
   distingue deux fichiers d'un même lot, et un nom coupé les confond. */
.remise-nom { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.remise-taille {
  flex: 0 0 auto; font-size: .82rem; color: rgba(255, 255, 255, .62);
  font-variant-numeric: tabular-nums;
}
.remise-vide { padding: 12px 0; color: rgba(255, 255, 255, .62); }

.remise-note { margin: 26px 0 0; font-size: .85rem; color: rgba(255, 255, 255, .58); }

/* Liste de fichiers à cocher, à l'émission d'un lien de remise. Une ligne par
   fichier plutôt qu'une liste déroulante à sélection multiple : celle-ci se
   manipule mal, n'affiche jamais plus de quelques lignes, et perd tout sur un
   clic malheureux. */
.choix { list-style: none; margin: 0 0 4px; padding: 0; }
.choix li + li { margin-top: 2px; }
.choix label {
  display: flex; align-items: baseline; gap: 10px;
  padding: 7px 10px; border-radius: 8px; cursor: pointer;
}
.choix label:hover { background: var(--surface-2, rgba(0, 0, 0, .04)); }
.choix label span:first-of-type { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.choix input { flex: 0 0 auto; margin: 0; }

/* Retirer un fichier : une action destructrice au milieu d'une liste de
   lecture. Elle se signale par sa couleur et par rien d'autre, pour ne pas
   peser autant qu'un bouton alors qu'on la rencontre à chaque ligne. */
form.inline { display: inline; }
.link-danger {
  background: none; border: 0; padding: 0 0 0 10px; box-shadow: none;
  color: var(--danger, #b3261e); font: inherit; font-size: .82rem; cursor: pointer;
}
.link-danger:hover, .link-danger:focus-visible { text-decoration: underline; background: none; }

/* Un bouton désactivé doit se voir désactivé. Les trois pages qui portent le
   téléverseur affichaient « Envoyer » en pleine couleur avant qu'un seul
   fichier ne soit choisi : on clique, rien ne se passe, et l'on cherche la
   panne. Le défaut était ancien, il se voyait moins sur une page qu'on ne
   consulte qu'une fois. */
button:disabled, .btn:disabled, button[disabled], .btn[disabled] {
  opacity: .45; cursor: not-allowed; filter: saturate(.4);
}
button:disabled:hover, .btn:disabled:hover { transform: none; box-shadow: none; }

/* Liste de fichiers à cocher, à l'émission d'un lien de remise. Une ligne par
   fichier plutôt qu'une liste déroulante à sélection multiple : celle-ci se
   manipule mal, n'affiche jamais plus de quelques lignes, et perd tout sur un
   clic malheureux. */
.choix { list-style: none; margin: 0 0 4px; padding: 0; }
.choix li + li { margin-top: 2px; }
.choix label {
  display: flex; align-items: baseline; gap: 10px;
  padding: 7px 10px; border-radius: 8px; cursor: pointer;
}
.choix label:hover { background: var(--surface-2, rgba(0, 0, 0, .04)); }
.choix label span:first-of-type { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.choix input { flex: 0 0 auto; margin: 0; }

/* Retirer un fichier : une action destructrice au milieu d'une liste de
   lecture. Elle se signale par sa couleur et par rien d'autre, pour ne pas
   peser autant qu'un bouton alors qu'on la rencontre à chaque ligne. */
form.inline { display: inline; }
.link-danger {
  background: none; border: 0; padding: 0 0 0 10px; box-shadow: none;
  color: var(--danger, #b3261e); font: inherit; font-size: .82rem; cursor: pointer;
}
.link-danger:hover, .link-danger:focus-visible { text-decoration: underline; background: none; }

/* Un bouton désactivé doit se voir désactivé. Les trois pages qui portent le
   téléverseur affichaient « Envoyer » en pleine couleur avant qu'un seul
   fichier ne soit choisi : on clique, rien ne se passe, et l'on cherche la
   panne. Le défaut était ancien, il se voyait moins sur une page qu'on ne
   consulte qu'une fois. */
button:disabled, .btn:disabled, button[disabled], .btn[disabled] {
  opacity: .45; cursor: not-allowed; filter: saturate(.4);
}
button:disabled:hover, .btn:disabled:hover { transform: none; box-shadow: none; }






/* --- L'anneau de progression -----------------------------------------------
   Un trait qui se referme autour d'un chiffre, et rien qui bouge d'autre.
   Demande de l'exploitant, et elle a sa raison : une animation qu'on croise
   mille fois cesse d'informer pour devenir un bruit de fond.

   En SVG : il n'y a pas d'image à calculer, seulement un arc dont la longueur
   change. Net à toute densité d'écran, sans boucle de rendu. */
.anneau {
  position: relative; display: grid; place-items: center;
  width: 190px; max-width: 100%; margin: 28px auto 0;
  aspect-ratio: 1;
}
.anneau svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* Le départ à midi. Sans cette rotation, l'arc naît à trois heures, là où
     personne ne commence à lire un cadran. */
  transform: rotate(-90deg);
}
.anneau circle { fill: none; stroke-width: 4; stroke-linecap: round; }
.anneau-fond { stroke: rgba(255, 255, 255, .14); }
.anneau-trait {
  stroke: #fff;
  transition: stroke-dashoffset .45s cubic-bezier(.22, .61, .36, 1);
}
@media (prefers-reduced-motion: reduce) { .anneau-trait { transition: none; } }

.anneau-pct {
  margin: 0; font-size: 2.4rem; font-weight: 600; line-height: 1;
  letter-spacing: -.02em; color: #fff;
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Sous le chiffre, à l'intérieur de l'anneau : c'est la même information, et
   la séparer de deux centimètres obligerait l'œil à faire l'aller-retour. */
.anneau-octets {
  position: absolute; top: 58%; left: 0; right: 0;
  margin: 0; text-align: center;
  font-size: .8rem; color: rgba(255, 255, 255, .62);
  font-variant-numeric: tabular-nums;
}

/* --- Le bouton d'envoi -----------------------------------------------------
   La flèche monte, parce que c'est ce que font les fichiers. Au repos elle
   attend ; au survol elle s'élance une fois ; pendant l'envoi elle monte en
   boucle. Trois états, trois informations. */
.btn.envoi {
  display: inline-flex; align-items: center; gap: 9px;
  padding-inline: 22px;
}
.btn.envoi .fleche, .btn.envoi .croix {
  width: 18px; height: 18px; flex: none;
  transition: transform .18s ease;
}
.btn.envoi:hover:not(:disabled) .fleche,
.btn.envoi:focus-visible:not(:disabled) .fleche { transform: translateY(-3px); }

/* Une seule envolée, au clic. Le bouton devient « Cancel » juste après : c'est
   le seul instant où ce geste a un sens, et il dit que quelque chose part. */
.btn.envoi[data-lance="1"] .fleche {
  animation: fleche-monte .42s cubic-bezier(.4, 0, .2, 1) 1;
}
@keyframes fleche-monte {
  0%   { transform: translateY(0); opacity: 1; }
  55%  { transform: translateY(-14px); opacity: 0; }
  56%  { transform: translateY(9px); opacity: 0; }
  100% { transform: translateY(0); opacity: 1; }
}

/* L'abandon n'est pas l'action de la page : il en est la sortie. Il perd donc
   la surface pleine et ne garde qu'un filet, comme les autres choix. */
body.page-depot .btn.envoi[data-etat="abandon"],
body.page-remise .btn.remise-annuler {
  background: transparent; color: #fff;
  border-color: rgba(255, 255, 255, .42);
}
body.page-depot .btn.envoi[data-etat="abandon"]:hover:not(:disabled),
body.page-remise .btn.remise-annuler:hover:not(:disabled) {
  border-color: #fff; box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
  .btn.envoi .fleche, .btn.envoi .croix {
    animation: none; transition: none; transform: none;
  }
}

/* --- Le dépôt sur le fond des projets --------------------------------------
   La page de dépôt reprend la coque de la remise. Ce qui suit rhabille son
   téléverseur pour ce fond : le rectangle de dépôt, les listes et les boutons
   étaient dessinés pour une surface opaque. */
.remise-carte.depot { width: min(42rem, 100%); }
.remise-carte.depot h1 { margin-bottom: 6px; }

.remise-carte .depot-zone {
  border: 1px dashed rgba(255, 255, 255, .26);
  background: rgba(255, 255, 255, .05);
  border-radius: 14px; padding: 26px 20px; margin: 22px 0 0;
  text-shadow: none;
}
.remise-carte .depot-zone p { color: rgba(255, 255, 255, .78); }
.remise-carte .depot-zone .btn { text-shadow: none; }

.remise-carte .upload-list {
  text-align: left; margin-top: 18px; text-shadow: none;
}
.remise-carte .form-actions { justify-content: center; margin-top: 22px; }
.remise-carte .meta, .remise-carte .hint { color: rgba(255, 255, 255, .62); }
.remise-carte .depot-fini {
  margin-top: 18px; color: rgba(255, 255, 255, .86);
}

/* Le voile de glisser-déposer. Il couvre la page entière parce que la page
   entière est la cible : quelqu'un qui arrive avec un dossier de cent fichiers
   ne doit pas avoir à viser un rectangle. */
.depot-voile {
  position: fixed; inset: 0; z-index: 40;
  display: grid; place-items: center;
  background: rgba(10, 10, 24, .72);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  pointer-events: none;
}
.depot-voile p {
  margin: 0; padding: 28px 44px;
  border: 2px dashed rgba(255, 255, 255, .5); border-radius: 18px;
  color: #fff; font-size: 1.25rem; font-weight: 600; letter-spacing: -.01em;
}

/* L'attente de l'archive, sur la page de remise. Une phrase, sans chiffre :
   le téléchargement est mené par le navigateur, et en afficher un qu'on ne
   mesure pas serait un mensonge agréable. */
.remise-attente {
  margin: 0 0 22px; font-size: .9rem; color: rgba(255, 255, 255, .72);
}


/* --- Les boutons des pages publiques ---------------------------------------
   Le lilas est la couleur de l'outil interne : il désigne l'accent d'une
   console qu'on habite toute la journée. Sur une page publique posée sur un
   plan de film, il se met à trancher avec l'image au lieu de s'y poser, et ces
   pages n'ont qu'un geste chacune. Elles parlent donc la même langue que
   « Download everything », qui est blanc depuis le premier jour : une surface
   pleine pour l'action, un simple filet pour l'autre choix. */
body.page-depot .btn, body.page-depot button.btn,
body.page-remise .btn, body.page-remise button.btn {
  background: #fff; background-image: none; color: #111;
  border-color: transparent; box-shadow: none; text-shadow: none;
  font-weight: 600;
  transition: transform .12s ease, box-shadow .12s ease;
}
body.page-depot .btn:hover:not(:disabled),
body.page-depot .btn:focus-visible:not(:disabled),
body.page-remise .btn:hover:not(:disabled),
body.page-remise .btn:focus-visible:not(:disabled) {
  filter: none; transform: translateY(-1px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
}
body.page-depot .btn.ghost, body.page-remise .btn.ghost {
  background: transparent; color: #fff;
  border-color: rgba(255, 255, 255, .34);
}
body.page-depot .btn.ghost:hover:not(:disabled),
body.page-depot .btn.ghost:focus-visible:not(:disabled),
body.page-remise .btn.ghost:hover:not(:disabled),
body.page-remise .btn.ghost:focus-visible:not(:disabled) {
  border-color: rgba(255, 255, 255, .62); box-shadow: none; transform: translateY(-1px);
}
body.page-depot .btn:focus-visible,
body.page-remise .btn:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }



/* --- La liste dépliable de la remise ---------------------------------------
   Douze lignes dépliées d'office font une page qu'on parcourt ; repliées,
   elles laissent le geste principal seul à l'écran, et celui qui ne veut
   qu'une affiche la trouve en un clic. */
.remise-details { text-align: left; }
.remise-details > summary {
  list-style: none; cursor: pointer;
  padding: 11px 16px; border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, .18);
  color: rgba(255, 255, 255, .84); font-size: .9rem; text-shadow: none;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.remise-details > summary::-webkit-details-marker { display: none; }
.remise-details > summary::after {
  content: ""; width: 8px; height: 8px; flex: none;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .18s ease;
}
.remise-details[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.remise-details > summary:hover { border-color: rgba(255, 255, 255, .34); color: #fff; }
.remise-details[open] > summary { margin-bottom: 10px; }

/* Ce qui est en train de descendre, nommé sous l'anneau. Sans ce nom, on ne
   sait pas lequel des douze fichiers on regarde. */
.remise-quoi {
  margin: 12px 0 0; font-size: .85rem; color: rgba(255, 255, 255, .72);
  overflow-wrap: anywhere;
}
.remise-annuler { margin-top: 16px; }
.remise .anneau { margin-top: 24px; }
.remise [data-erreur] { margin-top: 14px; }

@media (prefers-reduced-motion: reduce) {
  .remise-details > summary::after { transition: none; }
}

/* --- Le sélecteur liquide ---------------------------------------------------
   Un bouton « plus » d'où se détachent deux pastilles, dossier et fichier, qui
   semblent s'arracher de lui comme des gouttes.

   L'effet est celui de « liquid-gooey » de Jakub Antalik (MIT), repris ici en
   SVG et CSS plutôt qu'en React, que ce projet n'a pas. Ses réglages sont les
   siens : flou de six pixels, contraste dix-huit pour une ordonnée de moins
   sept — c'est ce couple qui fait fondre deux formes l'une dans l'autre au lieu
   de les flouter. La course reprend la sienne aussi : cinq cent cinquante
   millisecondes sur une courbe qui dépasse puis revient, et quarante
   millisecondes de retard sur la seconde pastille, ce qui suffit à ce que les
   deux ne partent pas comme un seul bloc. */
.gooey { position: relative; display: grid; place-items: center; height: 168px; }
.gooey-corps {
  position: relative; width: 64px; height: 64px;
  filter: url(#goo-arcane);
}
.gooey-corps .rond {
  position: absolute; inset: 0; margin: auto;
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center;
  background: #fff; color: #111; border: 0; cursor: pointer;
  box-shadow: none; padding: 0;
  transition: transform 550ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Les pastilles reposent sous le bouton principal et n'en sortent qu'ouvertes.
   Sans « pointer-events », elles resteraient cliquables sous lui. */
.gooey-corps .rond[data-choix] { pointer-events: none; }
.gooey[data-ouvert="1"] .rond[data-choix] { pointer-events: auto; }
/* Un triangle qui pointe vers le bas : le bouton d'appel en bas au centre, et
   les deux choix de part et d'autre, à la même hauteur. Symétrique, donc
   équilibré, et surtout assez écarté pour que les deux pictogrammes ne se
   frôlent pas — cent seize pixels séparent leurs centres. Le cou de goutte se
   forme quand même vers le bouton du bas, qui n'est qu'à soixante-dix. */
.gooey[data-ouvert="1"] .rond[data-choix="dossier"] { transform: translate(-58px, -40px); }
.gooey[data-ouvert="1"] .rond[data-choix="fichiers"] { transform: translate(58px, -40px); }
.gooey-corps .rond[data-choix="fichiers"] { transition-delay: 40ms; }
.rond-principal { z-index: 2; }
.rond svg { width: 24px; height: 24px; }
/* La croix devient un plus par simple rotation : c'est le même trait, et l'œil
   suit la bascule au lieu de constater un remplacement. */
.rond-principal svg { transition: transform 420ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.gooey[data-ouvert="1"] .rond-principal svg { transform: rotate(135deg); }
.gooey-corps .rond:focus-visible { outline: 2px solid #fff; outline-offset: 4px; }

@media (prefers-reduced-motion: reduce) {
  .gooey-corps .rond, .rond-principal svg { transition: none; }
}

/* --- D'un volet à l'autre --------------------------------------------------
   Un fondu court et une montée de quelques pixels, rien de plus. Le balayage
   qui s'y trouvait faisait un effet de rideau : on regardait la transition au
   lieu de regarder ce qu'elle amenait. */
.scene { position: relative; }
.volet[data-entre] { animation: volet-parait 280ms cubic-bezier(.22, .61, .36, 1) backwards; }
@keyframes volet-parait {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .volet[data-entre] { animation: none; }
}

/* La vitesse sous l'anneau. « ça fait pro », et surtout ça répond à la seule
   question qu'on se pose devant une barre qui avance : combien de temps
   encore. */
.anneau-vitesse {
  margin: 10px 0 0; font-size: .85rem; color: rgba(255, 255, 255, .66);
  font-variant-numeric: tabular-nums;
}
.remise-merci {
  margin: 14px 0 0; font-size: .95rem; color: #fff;
}
.remise-merci span { color: rgba(255, 255, 255, .7); display: block; font-size: .85rem; }

/* L'invite sous le sélecteur. Elle dit le second chemin — glisser — sans le
   mettre au même rang que le premier, qui est le bouton juste au-dessus. */
.depot-invite {
  margin: 4px 0 18px; font-size: .88rem; color: rgba(255, 255, 255, .6);
}
.remise-carte.depot .upload-list { margin-top: 6px; }
.remise-carte.depot .form-actions { margin-top: 22px; justify-content: center; }


/* Le téléchargement fini, la page se vide.
   Il ne reste que la marque, en tête de page, et le remerciement. Le reste —
   le titre, la ligne de présentation, l'anneau, la date de péremption — ne
   s'adresse qu'à quelqu'un qui n'a pas encore son fichier. */
.remise-carte[data-fini] > :not(.remise) { display: none; }
.remise-carte[data-fini] .anneau,
.remise-carte[data-fini] .anneau-vitesse,
.remise-carte[data-fini] .remise-quoi,
.remise-carte[data-fini] .form-actions { display: none; }
.remise-carte[data-fini] .remise-merci { margin: 0; font-size: 1.15rem; }
.remise-carte[data-fini] .remise-merci span { margin-top: 6px; font-size: .9rem; }

/* L'avertissement quand le navigateur refuse d'écrire à l'endroit choisi. Il
   accompagne le téléchargement au lieu de l'arrêter : le fichier arrivera. */
.remise-avis {
  margin: 12px auto 0; max-width: 36ch;
  font-size: .82rem; line-height: 1.5; color: var(--warm);
}
.remise-carte[data-fini] .remise-avis { display: none; }

/* Retirer un fichier de la sélection, avant l'envoi. On se trompe de fichier,
   on veut remplacer une image par une autre : sans ce bouton il fallait tout
   désélectionner et recommencer. */
.upload-row { grid-template-columns: 1fr 90px 140px 90px 28px; }
.upload-retirer {
  background: none; border: 0; padding: 4px; cursor: pointer; box-shadow: none;
  color: rgba(255, 255, 255, .4); line-height: 0; border-radius: 6px;
}
.upload-retirer svg { width: 14px; height: 14px; }
.upload-retirer:hover, .upload-retirer:focus-visible {
  color: #fff; background: rgba(255, 255, 255, .12);
}
.upload-row.error .upload-state { overflow-wrap: anywhere; }

/* Le formulaire d'adoption d'un ancien lien : une ligne, dans le bandeau. */
.inline-form { display: flex; gap: 10px; align-items: center; margin-top: 12px; }
.inline-form input { flex: 1 1 auto; min-width: 0; }

/* Les certificats d'une demande : le geste de les aller chercher, et la liste
   de ce qu'on n'a pas retenu. Un lot Dolby porte la même cabine sous cinq
   fichiers ; il faut pouvoir lire lequel des cinq a servi. */
.certificats-relever {
  display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap;
  margin-bottom: 14px;
}
.certificats-relever .hint { flex: 1 1 24ch; min-width: 0; margin: 0; }
.ecartes { list-style: none; padding: 0; margin: 10px 0 0; display: grid; gap: 10px; }
.ecartes li {
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px;
  background: rgba(255, 255, 255, .02);
}
.ecartes .hint { margin: 4px 0 0; }

/* ===========================================================================
   L'ESPACE D'UN CLIENT
   ===========================================================================
   Une coquille à part de la console du laboratoire, et pensée depuis le
   téléphone : c'est là qu'elle sera ouverte, dans un couloir, entre deux
   projections. Les largeurs montent ensuite pour le bureau, jamais l'inverse.

   Trois règles tenues partout dans ce bloc :
     — la cible touchable ne descend pas sous 44 px ;
     — le retour d'un geste part de l'appui, pas du relâchement ;
     — l'espacement est en « rem », pour qu'un réglage de taille de texte plus
       grand écarte la mise en page au lieu de la casser.
   =========================================================================== */
.espace {
  /* Pas de rail : la barre latérale de la console n'existe pas ici. */
  display: block;
  padding: 0;
  background: var(--bg);
}

/* La barre est une matière posée devant le contenu, qui défile dessous. Un
   trait d'un pixel aurait dit « deux zones » ; la matière dit « une seule, et
   celle-ci est devant ». */
.espace-barre {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
  padding: calc(env(safe-area-inset-top) + .5rem) max(1rem, env(safe-area-inset-left))
           .5rem max(1rem, env(safe-area-inset-right));
  background: var(--chrome);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 1px solid var(--chrome-edge);
}
/* Le bandeau d'essai est fixe et se poserait sur la marque. Il n'existe qu'en
   développement, mais une capture d'écran faite là-dessus ment sur la mise en
   page de production : on lui laisse donc sa place. */
.bandeau-essai ~ .espace-barre { padding-top: calc(env(safe-area-inset-top) + 1.5rem); }

.espace-marque { display: inline-flex; align-items: center; flex: none; }
/* Le même traitement que partout ailleurs sur le site : la marque est fournie
   en noir, et s'inverse pour les fonds sombres. Sans lui elle disparaissait
   presque entièrement dans le bleu de la barre. */
.espace-marque img {
  height: 26px; width: auto; display: block;
  filter: invert(1); opacity: .92;
}
.espace-quitter {
  flex: none; font-size: .82rem; color: var(--muted);
  min-height: 44px; display: inline-flex; align-items: center;
}
.espace-quitter:hover { color: var(--text); }

.espace-corps {
  max-width: 44rem; margin: 0 auto;
  padding: 1.25rem max(1rem, env(safe-area-inset-left))
           calc(env(safe-area-inset-bottom) + 3rem) max(1rem, env(safe-area-inset-right));
}

/* Le crénage se resserre quand le corps grandit : des lettres réglées pour du
   texte courant se lisent trop écartées à 2 rem. */
.espace-h1 {
  font-size: clamp(1.5rem, 6vw, 2.125rem);
  line-height: 1.08;
  letter-spacing: -0.021em;
  font-weight: 650;
  margin: .25rem 0 1.25rem;
}
.espace-dit { color: var(--muted); line-height: 1.5; margin: -.75rem 0 1.25rem; }

/* --- Les deux portes ----------------------------------------------------- */
/* « 1fr » sur les rangées : toutes prennent la hauteur de la plus haute. C'est
   ce qui tient la promesse d'un gabarit unique sans rogner le texte — un titre
   de film qui passe à la ligne agrandit alors toutes les cartes de l'écran au
   lieu de faire la sienne plus grande que les autres. */
.portes { display: grid; gap: .875rem; grid-auto-rows: 1fr; }
@media (min-width: 40rem) { .portes { grid-template-columns: 1fr 1fr; } }

/* L'icône à côté du texte et non au-dessus : au-dessus, elle fait un troisième
   étage dans une tuile qui n'en demandait que deux, et les deux tuiles ne se
   ressemblaient plus dès que l'une des deux lignes de texte passait à la
   ligne.

   Le gabarit est fixe. Une boîte qui prend la taille de son contenu donne deux
   boîtes différentes pour deux choses de même rang, et c'est ce désordre qui se
   voit en premier sur un téléphone. */
.porte {
  display: grid; align-content: center;
  grid-template-columns: auto 1fr; grid-template-rows: auto auto;
  column-gap: .95rem; row-gap: .2rem;
  min-height: 7.25rem; padding: 1.15rem 1.25rem;
  border: 1px solid var(--line); border-radius: 18px;
  background: var(--raised); box-shadow: var(--lift-2);
  color: var(--text); text-decoration: none;
  transition: transform var(--tempo-press) var(--courbe),
              box-shadow var(--tempo) var(--courbe);
}
.porte:hover { text-decoration: none; box-shadow: var(--lift-3); }
/* Le retour part de l'appui. Attendre le relâchement pour marquer la tuile
   fait tomber la sensation de direct. */
.porte:active { transform: scale(.975); box-shadow: var(--lift-1); }
.porte-ic {
  grid-row: 1 / 3; align-self: center;
  width: 26px; height: 26px; color: var(--accent); flex: none;
}
.porte-nom { font-size: 1.2rem; font-weight: 640; letter-spacing: -0.012em; }
.porte-dit { color: var(--muted); font-size: .9rem; line-height: 1.45; }

/* --- My files ------------------------------------------------------------ */
.projet-neuf { display: flex; gap: .6rem; margin-bottom: 1.5rem; }
.projet-neuf input { flex: 1 1 auto; min-width: 0; min-height: 44px; }
.projet-neuf .btn { flex: none; }

.projet {
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); box-shadow: var(--lift-1);
  padding: 1rem; margin-bottom: 1rem;
}
.projet-tete h2 {
  font-size: 1.1rem; font-weight: 640; letter-spacing: -0.01em; margin: 0;
}
.projet-dit { color: var(--muted); font-size: .84rem; margin: .2rem 0 .9rem; }

.variantes { list-style: none; padding: 0; margin: 0 0 .9rem; display: grid; gap: .6rem; }
.variante {
  display: grid; gap: .5rem;
  grid-template-columns: 1fr auto;
  align-items: start;
  padding: .75rem; border-radius: 12px;
  background: rgba(255, 255, 255, .03);
}
.variante-dit { display: grid; gap: .15rem; min-width: 0; }
.variante-nom { font-weight: 560; }
.variante-piste { color: var(--muted); font-size: .82rem; }
.variante-etat {
  font-size: .78rem; font-weight: 600; letter-spacing: .02em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
.variante[data-etat="ready"] .variante-etat { color: var(--vert-systeme); }
.variante[data-etat="failed"] .variante-etat { color: var(--error); }
.variante[data-etat="purged"] { opacity: .55; }
.variante-gestes {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: 1fr 1fr; gap: .5rem;
}
.variante-gestes > * { min-height: 44px; }
.variante-gestes .btn { text-align: center; }
/* Ouverte, la commande prend la ligne entière : son contenu n'a rien à faire
   dans une demi-colonne. Et la rangée passe alors à une seule colonne, sinon
   « Download » restait seul sur une demi-largeur au-dessus — le décalage même
   qu'on cherchait à faire disparaître. */
.variante-plus[open] { grid-column: 1 / -1; }
.variante-gestes:has(.variante-plus[open]) { grid-template-columns: 1fr; }
.variante-plus summary { list-style: none; cursor: pointer; display: block; }
.variante-plus summary::-webkit-details-marker { display: none; }
.variante-plus-corps { display: grid; gap: .9rem; margin-top: .75rem; }
.variante-corriger { display: grid; gap: .5rem; }
.variante-corriger select { min-height: 44px; width: 100%; }
.variante-retirer {
  display: grid; gap: .6rem; padding: .8rem; border-radius: 10px;
  background: rgba(255, 138, 138, .07); border: 1px solid rgba(255, 138, 138, .2);
}
.variante-retirer .hint { margin: 0; }

.depot summary { list-style: none; cursor: pointer; display: inline-flex; }
.depot summary::-webkit-details-marker { display: none; }
.depot-corps { margin-top: .9rem; display: grid; gap: .75rem; }
.depot-choix { display: grid; gap: .6rem; }
@media (min-width: 30rem) { .depot-choix { grid-template-columns: 1fr 1fr; } }
.depot-choix label { display: grid; gap: .25rem; font-size: .84rem; color: var(--muted); }
.depot-choix select { min-height: 44px; }
.depot-nom { margin: 0; font-weight: 560; overflow-wrap: anywhere; }

/* La progression d'un fichier, dans le vert d'Apple : il veut dire « ça
   avance, et ça se passe bien ». */
.depot-barre {
  height: 6px; border-radius: 99px; overflow: hidden;
  background: rgba(255, 255, 255, .1);
}
.depot-barre > span {
  display: block; height: 100%; width: 0;
  background: var(--vert-systeme); border-radius: 99px;
  transition: width 180ms linear;
}

/* --- L'assistant --------------------------------------------------------- */
.jalons {
  list-style: none; display: flex; gap: .4rem; padding: 0;
  margin: 0 0 1.5rem;
}
.jalons li {
  height: 3px; flex: 1 1 0; border-radius: 99px;
  background: rgba(255, 255, 255, .12);
  transition: background var(--tempo) var(--courbe);
}
.jalons li[data-etat="fait"] { background: var(--accent); }
.jalons li[data-etat="ici"] { background: var(--text); }

/* Tant que le script n'a pas pris la main, les quatre panneaux sont là et le
   formulaire marche d'un seul envoi. « data-vivant » est posé par le script :
   c'est lui qui autorise à replier. */
.assistant[data-vivant] .etape[hidden] { display: none; }
.etape { margin-bottom: 2rem; }
.assistant[data-vivant] .etape { margin-bottom: 0; }
.assistant[data-vivant] .etape[data-sens] { animation: etape-entre 260ms var(--courbe) both; }
.assistant[data-vivant] .etape[data-sens="arriere"] { animation-name: etape-revient; }

/* Entrée et sortie par le même chemin : on avance, le panneau vient de la
   droite ; on revient, de la gauche. */
@keyframes etape-entre {
  from { opacity: 0; transform: translate3d(14px, 0, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes etape-revient {
  from { opacity: 0; transform: translate3d(-14px, 0, 0); }
  to   { opacity: 1; transform: none; }
}

.assistant-retour {
  background: none; border: 0; box-shadow: none; padding: .5rem 0;
  color: var(--muted); font: inherit; font-size: .9rem; cursor: pointer;
  min-height: 44px; display: inline-flex; align-items: center; gap: .2rem;
}
.assistant-retour:hover { color: var(--text); }
.assistant-retour span { font-size: 1.3em; line-height: 1; }

.cartes { display: grid; gap: .7rem; margin-bottom: 1rem; grid-auto-rows: 1fr; }
/* Toutes au même gabarit, qu'elles portent une explication ou non. Quatre
   réponses au même rang qui n'ont pas la même hauteur se lisent comme quatre
   réponses d'importance différente. */
.carte {
  display: grid; gap: .2rem; align-content: center;
  justify-items: start; text-align: left;
  min-height: 4.5rem; padding: .9rem 1rem;
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--raised); color: var(--text);
  font: inherit; cursor: pointer; box-shadow: var(--lift-1);
  transition: transform var(--tempo-press) var(--courbe),
              border-color var(--tempo) var(--courbe);
}
.carte:hover { border-color: rgba(255, 255, 255, .2); }
.carte:active { transform: scale(.985); }
.carte[data-choisi] { border-color: var(--accent); }
.carte-nom {
  font-size: 1.02rem; font-weight: 580;
  /* Un titre de film peut courir sur quatre lignes et faire de sa carte le
     double des autres. Deux lignes suffisent à le reconnaître. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.carte-dit { color: var(--muted); font-size: .85rem; line-height: 1.4; }

.calendrier {
  display: grid; gap: .75rem; padding: 1rem;
  border: 1px solid var(--line); border-radius: 14px; background: var(--surface);
}
.calendrier label { display: grid; gap: .25rem; font-size: .84rem; color: var(--muted); }
.calendrier input { min-height: 44px; }

/* --- Le lien, au bout ---------------------------------------------------- */
.fini { text-align: center; padding-top: 1.5rem; }
.fini-ic {
  width: 44px; height: 44px; color: var(--vert-systeme); margin-bottom: .75rem;
}
.fini .espace-dit { margin-top: -.75rem; }

.lien-copie {
  display: flex; gap: .5rem; margin: 1.5rem 0;
  text-align: left;
}
.lien-copie input {
  flex: 1 1 auto; min-width: 0; min-height: 44px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .82rem;
}
.lien-copie .btn { flex: none; }
.lien-copie .btn[data-fait] {
  background: rgba(48, 209, 88, .18); border-color: rgba(48, 209, 88, .4);
}

.recapitulatif {
  display: grid; grid-template-columns: auto 1fr; gap: .4rem .9rem;
  text-align: left; margin: 0 0 1.75rem;
  padding: 1rem; border: 1px solid var(--line); border-radius: 14px;
  background: var(--surface);
}
.recapitulatif dt { color: var(--muted); font-size: .84rem; }
.recapitulatif dd { margin: 0; font-size: .9rem; }

.fini-gestes { display: flex; gap: .6rem; justify-content: center; flex-wrap: wrap; }

/* --- Ce que le système demande ------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  /* Pas d'immobilité : un fondu court dit encore « ceci remplace cela », sans
     déplacement latéral. */
  .assistant[data-vivant] .etape[data-sens] { animation: etape-fondu 180ms ease both; }
  .porte, .carte { transition: none; }
  .porte:active, .carte:active { transform: none; }
  .depot-barre > span { transition: none; }
}
@keyframes etape-fondu { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-transparency: reduce) {
  .espace-barre {
    background: var(--bg); backdrop-filter: none; -webkit-backdrop-filter: none;
  }
}
@media (prefers-contrast: more) {
  .espace-barre { background: var(--bg); border-bottom-color: var(--text); }
  .porte, .carte, .projet { border-color: rgba(255, 255, 255, .34); }
}

/* Les boutons de l'espace parlent la même langue que les pages de dépôt et de
   remise : une surface pleine pour l'action, un simple filet pour l'autre
   choix. Le lilas d'accent sert à désigner, pas à commander ; il restait
   partout et finissait par ne plus rien signaler. */
body.espace .btn, body.espace button.btn {
  background: #fff; background-image: none; color: #111;
  border-color: transparent; box-shadow: none; font-weight: 600;
}
body.espace .btn:hover:not(:disabled), body.espace .btn:focus-visible:not(:disabled) {
  filter: none; transform: translateY(-1px); box-shadow: var(--lift-2);
}
body.espace .btn.ghost {
  background: transparent; color: var(--text); border-color: rgba(255, 255, 255, .28);
}
body.espace .btn.ghost:hover:not(:disabled) { border-color: rgba(255, 255, 255, .5); }
body.espace .btn.danger {
  background: transparent; color: var(--error); border-color: rgba(255, 122, 122, .45);
}
body.espace .btn:disabled { opacity: .5; }


/* Le bouton d'envoi n'existe que pour la page sans script, où les quatre
   panneaux s'affichent à la suite. Quand le script mène la danse, le dernier
   choix envoie déjà : laisser le bouton ferait cinq gestes pour quatre
   décisions, et le dernier n'aurait aucun effet visible. */
.assistant[data-vivant] [data-envoi] { display: none; }

/* --- Les liens partis ------------------------------------------------------
   Deux chiffres par ligne, et ils ne disent pas la même chose : les ouvertures
   sont le quota, la part visionnée est la réponse. Les mettre côte à côte au
   même rang est tout l'objet de cet écran. */
.partage {
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--surface); box-shadow: var(--lift-1);
  padding: 1rem; margin-bottom: 1rem;
}
.partage-tete {
  display: flex; gap: .75rem; align-items: start; justify-content: space-between;
}
.partage-dit { display: grid; gap: .15rem; min-width: 0; }
.partage-nom { font-weight: 620; letter-spacing: -0.008em; }
.partage-sous { color: var(--muted); font-size: .82rem; }
.partage-etiquette {
  font-size: .82rem; color: var(--text); opacity: .85; margin-top: .15rem;
}
.partage-etat {
  flex: none; font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
  padding-top: .15rem;
}
.partage[data-etat="live"] .partage-etat { color: var(--vert-systeme); }
.partage[data-etat="cut"] .partage-etat { color: var(--error); }
.partage[data-etat="exhausted"] .partage-etat,
.partage[data-etat="expired"] .partage-etat { color: var(--warm); }

.partage-chiffres {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem;
  margin: .9rem 0; padding: .75rem; border-radius: 12px;
  background: rgba(255, 255, 255, .03);
}
.partage-chiffres > div { display: grid; gap: .1rem; min-width: 0; }
.partage-chiffres dt {
  font-size: .7rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
.partage-chiffres dd { margin: 0; font-size: .95rem; font-weight: 560; }

.partage-rien { margin: .9rem 0; }
.partage-passages, .partage-gerer { display: block; }
.partage-passages summary, .partage-gerer summary {
  list-style: none; cursor: pointer; display: block;
  text-align: center; min-height: 44px;
}
.partage-passages summary::-webkit-details-marker,
.partage-gerer summary::-webkit-details-marker { display: none; }
.partage-passages { margin-bottom: .5rem; }

.passages { list-style: none; padding: 0; margin: .75rem 0 0; display: grid; gap: .4rem; }
.passages li {
  display: flex; justify-content: space-between; gap: .75rem; flex-wrap: wrap;
  padding: .55rem .7rem; border-radius: 10px;
  background: rgba(255, 255, 255, .03); font-size: .85rem;
}
.passage-date { color: var(--muted); }
.passage-part { font-weight: 560; }

.partage-gerer-corps { display: grid; gap: 1rem; margin-top: .75rem; }
.partage-gerer-corps form { display: grid; gap: .6rem; }
.partage-gerer-corps .hint { margin: 0; }
.prolonge { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.prolonge label { display: grid; gap: .25rem; font-size: .8rem; color: var(--muted); }
.prolonge input { min-height: 44px; width: 100%; }
.partage-etiqueter { grid-template-columns: 1fr auto; display: grid !important; }
.partage-etiqueter input { min-height: 44px; min-width: 0; }
.partage-couper {
  padding: .8rem; border-radius: 10px;
  background: rgba(255, 138, 138, .07); border: 1px solid rgba(255, 138, 138, .2);
}

.fini-etiquette {
  display: grid; grid-template-columns: 1fr auto; gap: .5rem;
  margin: 0 0 1.5rem; text-align: left;
}
.fini-etiquette input { min-height: 44px; min-width: 0; }

/* --- La place qui reste ----------------------------------------------------
   Lue avant de choisir un fichier. Un refus au moment de déposer surprend ; une
   jauge qu'on a sous les yeux ne surprend personne. */
.place { margin-bottom: 1.25rem; }
.place-barre {
  height: 6px; border-radius: 99px; overflow: hidden;
  background: rgba(255, 255, 255, .1);
}
.place-barre > span {
  display: block; height: 100%; border-radius: 99px;
  background: var(--vert-systeme);
  transition: width var(--tempo) var(--courbe);
}
/* Au-dessus du forfait, la jauge change de couleur sans rien bloquer : le
   dépassement se règle par une conversation, et c'est le plafond qui refuse. */
.place[data-serre="1"] .place-barre > span { background: var(--warm); }
.place-dit { margin: .45rem 0 0; font-size: .84rem; color: var(--muted); }
.place-dit strong { color: var(--text); font-weight: 600; }

/* Le mur d'une remise protégée. Un champ, un bouton, et rien qui renseigne sur
   ce qu'il y a derrière : un courriel transféré doit pouvoir l'être encore
   sans rien apprendre à personne. */
.remise-mdp { display: grid; gap: .75rem; max-width: 22rem; margin: 1.5rem auto 0; }
.remise-mdp input { min-height: 46px; text-align: center; font-size: 1rem; }
.remise-erreur { margin: 0; color: var(--error); font-size: .88rem; text-align: center; }

/* ---------------------------------------------------------------------------
   Les navettes

   Deux écrans très différents. Celui de l'atelier reprend la console et n'a
   rien demandé de particulier. Celui du porteur se tient à bout de bras, dans
   un couloir, avec un disque dans l'autre main : tout y est grand, et il n'y a
   jamais qu'une seule chose à faire.
   --------------------------------------------------------------------------- */

body.navette {
  background: var(--bg);
  color: var(--text);
  margin: 0;
  /* Un téléphone à encoche : le contenu ne passe pas dessous. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}
.navette-shell {
  max-width: 560px;
  margin: 0 auto;
  padding: 24px 20px 56px;
}
.navette-marque {
  display: block; width: 132px; height: auto; margin: 0 0 28px;
  /* Le fichier de marque est noir : sur fond sombre il disparaît. Même
     traitement que la page de visionnage, qui s'ouvre dans les mêmes
     conditions. */
  filter: invert(1); opacity: .88;
}

.navette-tete { margin-bottom: 28px; }
.navette-sur {
  margin: 0 0 4px; color: var(--muted);
  font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
}
.navette-tete h1 {
  /* Un titre d'écran se resserre quand il grandit : à cette taille, un
     interlettrage neutre fait flotter les lettres. */
  margin: 0; font-size: clamp(28px, 8vw, 38px); line-height: 1.05; letter-spacing: -.02em;
}
.navette-role { margin: 6px 0 0; color: var(--muted); font-size: 15px; }

/* Le geste. Quarante-huit pixels de haut au minimum : un pouce ne vise pas
   mieux que cela, et l'on scanne souvent d'une seule main. */
.navette-bouton {
  display: block; width: 100%; box-sizing: border-box;
  min-height: 56px; padding: 16px 20px;
  border: 0; border-radius: 14px;
  background: var(--accent); color: #fff;
  font: inherit; font-size: 17px; font-weight: 600; text-align: center; text-decoration: none;
  cursor: pointer;
  /* La réponse part à l'appui, pas au relâchement : attendre le doigt qui se
     lève donne l'impression d'une page morte. */
  transition: transform 100ms ease-out, background-color 100ms ease-out;
}
.navette-bouton:active { transform: scale(.975); }
.navette-bouton.fantome {
  background: transparent; color: var(--text);
  border: 1px solid var(--line);
}
.navette-bouton + .navette-bouton, .navette-defaire { margin-top: 12px; }

.navette-visee { margin: 0 0 16px; }
.navette-visee video {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: 14px; background: #000; display: block;
}
.navette-aide { margin: 10px 0; color: var(--muted); font-size: 14px; line-height: 1.5; }

.navette-saisie { margin-top: 16px; }
.navette-saisie label {
  display: block; margin-bottom: 8px; color: var(--muted); font-size: 13px;
}
/* Le placeholder garde sa casse : capitalisé, il se lit comme une valeur
   déjà saisie plutôt que comme un exemple. */
.navette-saisie input::placeholder { text-transform: none; letter-spacing: .02em; opacity: .5; }
.navette-saisie input {
  width: 100%; box-sizing: border-box; min-height: 52px;
  padding: 14px 16px; margin-bottom: 12px;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--raised); color: var(--text);
  /* Seize pixels : en dessous, iOS grossit la page à la mise au point du
     champ, et l'on se retrouve avec une page décadrée. */
  font-size: 16px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em; text-transform: uppercase;
}
.navette-note { margin: 18px 0 0; color: var(--muted); font-size: 13px; line-height: 1.6; }

.navette-tenus { margin-top: 40px; }
.navette-tenus h2 {
  margin: 0 0 12px; font-size: 13px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted); font-weight: 600;
}
.navette-liste { list-style: none; padding: 0; margin: 0; display: grid; gap: 8px; }
.navette-liste li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px;
  background: rgba(255, 255, 255, .02);
}
.navette-nom { font-size: 16px; font-weight: 500; }
.navette-depuis { color: var(--muted); font-size: 13px; flex: none; }
.navette-vide { color: var(--muted); font-size: 14px; }

.navette-mot { padding: 14px 16px; border-radius: 12px; background: var(--raised); font-size: 15px; }
.navette-mot.error { border: 1px solid var(--error); }

/* La réponse après un scan. Elle se lit d'un coup d'œil, à bout de bras, sans
   chercher : c'est le seul contenu de l'écran. */
.navette-recu { text-align: center; padding: 32px 0 24px; }
.navette-coche {
  width: 72px; height: 72px; margin: 0 auto 20px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(74, 212, 160, .14); color: var(--ok);
}
.navette-coche svg { width: 36px; height: 36px; }
.navette-recu-deja .navette-coche { background: rgba(255, 255, 255, .06); color: var(--muted); }
.navette-recu h1 { margin: 0 0 8px; font-size: clamp(26px, 7vw, 34px); letter-spacing: -.02em; }
.navette-grand { margin: 0 0 6px; font-size: 18px; }

.navette-lien { word-break: break-all; margin: 10px 0; }
.navette-barre { opacity: .55; text-decoration: line-through; }
.navette-barre form, .navette-barre .badge { text-decoration: none; }

@media (prefers-reduced-motion: reduce) {
  .navette-bouton { transition: none; }
  .navette-bouton:active { transform: none; }
}

/* La visée au bureau : plus petite que sur le téléphone, puisqu'elle n'occupe
   qu'un champ de formulaire au milieu d'autres. */
.navette-visee-bureau { max-width: 420px; margin-top: 10px; }
.navette-visee-bureau video { aspect-ratio: 16 / 9; }

/* Le cliché : un bouton qui est en réalité un champ de fichier. Le champ
   lui-même reste caché, parce qu'un « Choisir un fichier » natif n'annonce
   pas qu'il ouvrira l'appareil photo. */
.navette-cliche { margin-top: 12px; }
.navette-cliche label { cursor: pointer; }
.navette-cliche label:focus-within { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------------------------------------------------------------------------
   La galerie de stills

   Une page qu'un producteur ouvre sur un iPad, le soir, et fait défiler. Rien
   d'autre que les photos : pas de navigation, pas de compte, pas d'outil. Ce
   qu'on regarde doit occuper l'écran, et ce qui sert à regarder doit se faire
   oublier.
   --------------------------------------------------------------------------- */

body.galerie {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  /* Un téléphone à encoche : le contenu ne passe pas dessous. */
  padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}
body.galerie-fige { overflow: hidden; }

.galerie-marque {
  display: block; width: 118px; height: auto;
  /* Le fichier de marque est noir : sur fond sombre il disparaît. */
  filter: invert(1); opacity: .86;
}

/* L'en-tête tient à l'écran et laisse les photos passer dessous : sur une
   page qu'on fait défiler longtemps, savoir où l'on est vaut mieux qu'un
   titre perdu de vue au troisième jour. */
.galerie-tete {
  padding: calc(18px + env(safe-area-inset-top)) 0 16px;
  /* Du thème, et non du bleu de minuit en dur : sur un fond clair, la barre
     restait sombre sous un texte devenu sombre. */
  background: var(--chrome);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border-bottom: 1px solid var(--line);
}
/* Le titre ne tient à l'écran que là où il y a la place. Sur un téléphone il
   prenait cent soixante-quinze pixels sur huit cents, en permanence, pour
   répéter un nom qu'on a lu une fois. Ce qu'on veut savoir en défilant, c'est
   quel jour on regarde — et c'est la ligne de la journée qui le dit. */
@media (min-width: 821px) {
  .galerie-tete { position: sticky; top: 0; z-index: 20; }
}
.galerie-tete-corps { max-width: 1680px; margin: 0 auto; padding: 0 clamp(16px, 3vw, 40px); }
.galerie-tete h1 {
  margin: 14px 0 0;
  /* Un titre se resserre quand il grandit : à cette taille, un interlettrage
     neutre fait flotter les lettres. */
  font-size: clamp(22px, 3.4vw, 34px); line-height: 1.1; letter-spacing: -.022em;
}
.galerie-intro {
  margin: 8px 0 0; color: var(--muted);
  font-size: clamp(14px, 1.4vw, 16px); line-height: 1.55; max-width: 62ch;
}

.galerie-corps { max-width: 1680px; margin: 0 auto; padding: 0 clamp(16px, 3vw, 40px) 72px; }
.galerie-journee { margin-top: clamp(28px, 4vw, 48px); }

.galerie-jour {
  display: flex; align-items: center; gap: 14px;
  /* Elle tient à l'écran pendant qu'on fait défiler la journée : sur une
     galerie de trois cents photos, c'est la seule chose qu'on cherche des
     yeux. Les marges négatives lui rendent la pleine largeur, que le
     rembourrage du corps lui avait prise. */
  position: sticky; top: 0; z-index: 10;
  margin: 0 calc(-1 * clamp(16px, 3vw, 40px)) 16px;
  padding: 10px clamp(16px, 3vw, 40px) 11px;
  /* Plus un filet qu'un pavé : le fond s'allège et c'est une ligne sous la
     barre qui la détache, pas un bloc de couleur. */
  background: var(--chrome);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  box-shadow: inset 0 -1px 0 var(--line);
  font-weight: 400; color: var(--text);
}

/* Le numéro porte la ligne. C'est de lui qu'on parle sur un plateau, et c'est
   lui qu'on retrouve des yeux en faisant défiler. */
.galerie-jour-num {
  flex: none;
  font-size: clamp(24px, 3.4vw, 31px); font-weight: 600; line-height: 1;
  /* Serré, comme tout caractère qui grandit ; et à chasse fixe, pour que 01
     et 10 ne déplacent pas ce qui suit d'une journée à l'autre. */
  letter-spacing: -.03em; font-variant-numeric: tabular-nums;
  color: var(--text);
}

.galerie-jour-corps { display: grid; gap: 1px; min-width: 0; }
.galerie-jour-label {
  font-size: 10px; font-weight: 600; letter-spacing: .13em;
  text-transform: uppercase; color: var(--muted);
}
.galerie-jour-date {
  font-size: 12.5px; font-weight: 500; letter-spacing: .005em;
  text-transform: none; color: var(--text); opacity: .88;
}

/* Le filet qui mène au compte. Il se réduit à rien quand la place manque. */
.galerie-jour-filet {
  flex: 1 1 auto; min-width: 0; height: 1px;
  background: var(--line);
}

.galerie-jour-compte {
  flex: none;
  font-size: 11px; font-weight: 600; letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  padding: 3px 9px; border: 1px solid var(--line); border-radius: 999px;
}

.galerie-grille {
  column-count: 4;
  column-gap: clamp(8px, 1vw, 14px);
}
@media (max-width: 1280px) { .galerie-grille { column-count: 3; } }
@media (max-width: 820px)  { .galerie-grille { column-count: 2; } }
@media (max-width: 460px)  { .galerie-grille { column-count: 1; } }

.galerie-tuile {
  display: block; width: 100%; padding: 0; margin: 0 0 clamp(8px, 1vw, 14px);
  border: 0; border-radius: 10px; overflow: hidden;
  background: rgba(255, 255, 255, .04);
  cursor: zoom-in; position: relative;
  /* La colonne ne coupe pas une photo en deux. */
  break-inside: avoid; -webkit-column-break-inside: avoid;
  /* La place est réservée avant que l'image n'arrive : sans cela la page
     saute à chaque chargement, et l'on perd la ligne qu'on regardait. */
  aspect-ratio: auto;
  transition: transform 140ms var(--courbe, ease-out), box-shadow 140ms ease-out;
}
.galerie-tuile::before { content: ""; display: block; padding-top: var(--ratio, 66.7%); }
.galerie-tuile img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* La réponse part à l'appui, pas au relâchement : attendre le doigt qui se
   lève donne l'impression d'une page morte. */
.galerie-tuile:active { transform: scale(.985); }
@media (hover: hover) {
  .galerie-tuile:hover { box-shadow: 0 10px 30px rgba(0, 0, 0, .45); }
}
.galerie-tuile:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.galerie-legende {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 22px 12px 10px; text-align: left;
  font-size: 12px; line-height: 1.4; color: #fff;
  background: linear-gradient(transparent, rgba(0, 0, 0, .66));
}

.galerie-vide { color: var(--muted); padding: 60px 0; text-align: center; }
.galerie-pied {
  padding: 28px 0 calc(28px + env(safe-area-inset-bottom));
  text-align: center; color: var(--muted); font-size: 12px; letter-spacing: .06em;
}

/* --- La visionneuse ------------------------------------------------------ */

.galerie-vue {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  background: rgba(8, 8, 20, .94);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.galerie-vue img {
  max-width: 100%; max-height: 100vh; max-height: 100dvh;
  object-fit: contain; display: block;
}
/* L'ouverture part de la tuile touchée, et non du centre de l'écran. */
.galerie-vue.ouvre { animation: galerie-vient 180ms cubic-bezier(.22, 1, .36, 1); }
@keyframes galerie-vient {
  from { opacity: 0; transform: scale(.94); }
  to   { opacity: 1; transform: scale(1); }
}

.galerie-vue-barre {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: center; gap: 12px;
  padding: 14px clamp(14px, 3vw, 28px) calc(14px + env(safe-area-inset-bottom));
  background: linear-gradient(transparent, rgba(0, 0, 0, .74));
}
.galerie-vue-mot { flex: 1; min-width: 0; margin: 0; font-size: 13px; color: #e9e7fb; }
.galerie-vue-mot span { color: inherit; }
/* En icônes : une barre de visionneuse se lit d'un coup d'œil au-dessus d'une
   photo, pas en déchiffrant deux étiquettes. Rondes et de quarante-quatre
   pixels, comme les flèches : c'est la plus petite cible qu'un pouce vise
   sans y penser. */
.galerie-vue-action {
  flex: none; width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: 1px solid rgba(255, 255, 255, .2); border-radius: 50%;
  background: rgba(255, 255, 255, .1); color: #fff;
  text-decoration: none; cursor: pointer;
  transition: background-color 120ms ease-out, transform 120ms ease-out;
}
.galerie-vue-action svg { width: 20px; height: 20px; display: block; }
@media (hover: hover) {
  .galerie-vue-action:hover { background: rgba(255, 255, 255, .2); }
}
.galerie-vue-action:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.galerie-vue-action:active { transform: scale(.97); }

.galerie-vue-fleche {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 52px; height: 52px; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .18);
  background: rgba(0, 0, 0, .42); color: #fff;
  font-size: 30px; line-height: 1; cursor: pointer;
}
.galerie-vue-fleche.precedent { left: clamp(8px, 2vw, 24px); }
.galerie-vue-fleche.suivant { right: clamp(8px, 2vw, 24px); }
/* Sur un écran tactile on balaie ; les flèches y prennent de la place pour
   rien et tombent sous le pouce. */
@media (hover: none) and (max-width: 820px) { .galerie-vue-fleche { display: none; } }

/* --- Le mur -------------------------------------------------------------- */

.galerie-mur {
  min-height: 100vh; min-height: 100dvh;
  display: grid; align-content: center; justify-items: start;
  gap: 14px; max-width: 420px; margin: 0 auto; padding: 32px 20px;
}
.galerie-mur h1 { margin: 10px 0 0; font-size: clamp(22px, 5vw, 28px); letter-spacing: -.02em; }
.galerie-mot { margin: 0; color: var(--muted); font-size: 15px; line-height: 1.6; }
.galerie-mdp { width: 100%; display: grid; gap: 12px; margin-top: 6px; }
.galerie-mdp input {
  width: 100%; box-sizing: border-box; min-height: 52px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--raised); color: var(--text);
  /* Seize pixels : en dessous, iOS grossit la page à la mise au point. */
  font-size: 16px;
}
.galerie-mdp button {
  min-height: 52px; border: 0; border-radius: 12px;
  background: var(--accent); color: #fff;
  font: inherit; font-size: 17px; font-weight: 600; cursor: pointer;
}
.galerie-mdp button:active { transform: scale(.98); }
.galerie-erreur { margin: 0; color: var(--error); font-size: 14px; }

/* Le bandeau d'instance d'essai est fixé au-dessus de tout : ce qui tient à
   l'écran doit se poser en dessous, sinon la ligne de la journée passe
   derrière lui et devient illisible. Même traitement que la barre de
   l'espace client, qui a rencontré le problème avant. */
.bandeau-essai ~ .galerie-tete { top: 23px; }
.bandeau-essai ~ .galerie-corps .galerie-jour { top: 23px; }

@media (prefers-reduced-motion: reduce) {
  .galerie-tuile, .galerie-vue-action, .galerie-mdp button { transition: none; }
  .galerie-tuile:active, .galerie-vue-action:active, .galerie-mdp button:active { transform: none; }
  .galerie-vue.ouvre { animation: none; }
}
/* La visionneuse ne suit pas le thème, et c'est voulu : on juge une image
   sur du noir, même quand la page autour est en papier. */
html.theme-paper .galerie-vue-mot { color: #e9e7fb; }

@media (prefers-reduced-transparency: reduce) {
  .galerie-tete, .galerie-jour {
    background: var(--bg); backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .galerie-vue { backdrop-filter: none; -webkit-backdrop-filter: none; background: #080814; }
}

/* Le dépôt des stills, côté atelier. */
.stills-depot { margin: 0 0 28px; max-width: 760px; }
.stills-depot-tete { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-bottom: 12px; }
.stills-depot-tete label[for="jour"] { color: var(--muted); font-size: 13px; }
.stills-depot-tete input[type=date] {
  min-height: 40px; padding: 8px 12px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--raised); color: var(--text); font: inherit; font-size: 15px;
}
.stills-zone {
  border: 1px dashed var(--line); border-radius: 12px;
  padding: 28px; text-align: center; color: var(--muted);
  transition: border-color 140ms ease-out, background-color 140ms ease-out;
}
.stills-zone.survol { border-color: var(--accent); background: rgba(125, 123, 245, .08); }
.stills-zone p { margin: 0; font-size: 14px; }
.stills-avance { margin: 10px 0 0; color: var(--muted); font-size: 13px; }

.stills-rang {
  display: grid; gap: 12px; margin-bottom: 28px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.stills-piece { margin: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.stills-piece img { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover; background: #000; }
.stills-piece figcaption { padding: 8px; display: grid; gap: 6px; }
.stills-piece figcaption form { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.stills-piece figcaption input {
  flex: 1; min-width: 0; padding: 6px 8px; font-size: 13px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--raised); color: var(--text);
}
@media (prefers-reduced-motion: reduce) { .stills-zone { transition: none; } }
