/* =============================================================================
   send.noxelstudio.com — feuille unique
   -----------------------------------------------------------------------------
   Meme famille que noxelstudio.com : encre noire sur papier, Space Grotesk pour
   le texte, DM Mono pour tout ce qui se lit comme une donnee (etiquettes,
   tailles, codes, horodatages). Un seul accent, le rouge d'enregistrement,
   reserve a l'action et aux reperes vivants — jamais a une phrase.

   La page s'inverse toute seule si le systeme est en theme sombre : c'est un
   outil, on s'en sert le soir, et un rectangle blanc en pleine nuit n'est pas
   un choix de design, c'est un oubli.

   REGLE : aucune couleur en dur hors du bloc de variables. Sinon la page ne
   sait plus s'inverser.
   ============================================================================= */

/* ---------- polices, hebergees ici ---------- */
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/dm-mono-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/dm-mono-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/dm-mono-500-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/dm-mono-500-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/space-grotesk-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/space-grotesk-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/space-grotesk-500-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/space-grotesk-500-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:700; font-display:swap;
  src:url('/fonts/space-grotesk-700-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:700; font-display:swap;
  src:url('/fonts/space-grotesk-700-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* ---------- variables ---------- */
:root {
  --paper:#f2f0ec;
  --paper-2:#ebe8e2;
  --ink:#12110f;
  --ink-2:rgba(18,17,15,.60);
  --ink-3:rgba(18,17,15,.38);
  --rule:rgba(18,17,15,.15);
  --rule-2:rgba(18,17,15,.07);
  --field:rgba(18,17,15,.035);
  --acc:#c81d10;
  --acc-ink:#fff;
  --ok:#1d6b3f;
  --shadow:0 1px 2px rgba(18,17,15,.05), 0 12px 34px -18px rgba(18,17,15,.35);

  --sans:'Space Grotesk',ui-sans-serif,system-ui,-apple-system,'Segoe UI',sans-serif;
  --mono:'DM Mono',ui-monospace,'SF Mono',Menlo,monospace;

  --col:min(980px,100% - 2.4rem);
  --r:3px;
  --ease:cubic-bezier(.2,.7,.3,1);
  --pop:cubic-bezier(.16,1.1,.3,1);
}

@media (prefers-color-scheme:dark) {
  :root {
    --paper:#100f0e;
    --paper-2:#181614;
    --ink:#ece9e3;
    --ink-2:rgba(236,233,227,.60);
    --ink-3:rgba(236,233,227,.36);
    --rule:rgba(236,233,227,.17);
    --rule-2:rgba(236,233,227,.08);
    --field:rgba(236,233,227,.05);
    --acc:#ff4a38;
    --acc-ink:#14100f;
    --ok:#4cc17d;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 18px 40px -22px rgba(0,0,0,.9);
  }
}

/* ---------- socle ---------- */
*,*::before,*::after { box-sizing:border-box; }
html { -webkit-text-size-adjust:100%; }
body {
  margin:0; min-height:100svh; background:var(--paper); color:var(--ink);
  font:400 16px/1.55 var(--sans);
  -webkit-font-smoothing:antialiased;
  padding-bottom:env(safe-area-inset-bottom);
  display:flex; flex-direction:column;
}
img,svg { display:block; max-width:100%; }
button,input,select,textarea { font:inherit; color:inherit; }
a { color:inherit; }
::selection { background:var(--acc); color:var(--acc-ink); }

/* Un liseré rouge en haut de page : le seul aplat de couleur du site. */
body::before {
  content:''; position:fixed; inset:0 0 auto 0; height:2px;
  background:var(--acc); z-index:60;
}

.wrap { width:var(--col); margin:0 auto; }
main { flex:1 1 auto; }

/* ---------- micro-etiquettes ---------- */
.lbl {
  font:500 10.5px/1 var(--mono); letter-spacing:.15em; text-transform:uppercase;
  color:var(--ink-3);
}
.mono { font-family:var(--mono); font-variant-numeric:tabular-nums; }
.dim  { color:var(--ink-2); }
.tiny { font-size:13px; }
.spacer { flex:1 1 auto; }

/* ---------- en-tete ---------- */
.top {
  display:flex; align-items:center; gap:.9rem; flex-wrap:wrap;
  padding:1.7rem 0 1.1rem;
}
.mark {
  font:700 clamp(22px,5vw,27px)/1 var(--sans); letter-spacing:-.035em; margin:0;
  display:flex; align-items:baseline; gap:.05em;
}
.mark span { color:var(--acc); }
.top .home {
  font:400 11px/1 var(--mono); letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink-3); text-decoration:none; border-bottom:1px solid transparent;
  padding-bottom:2px;
}
.top .home:hover { color:var(--ink); border-bottom-color:var(--ink); }

/* ---------- boutons ---------- */
.btn {
  display:inline-flex; align-items:center; justify-content:center; gap:.5rem;
  min-height:46px; padding:.65rem 1.25rem; border-radius:var(--r);
  border:1px solid var(--ink); background:transparent; color:var(--ink);
  font:500 14.5px/1 var(--sans); cursor:pointer; text-decoration:none;
  transition:background .16s var(--ease), color .16s var(--ease),
             border-color .16s var(--ease), transform .12s var(--ease), opacity .16s;
}
.btn:hover { background:var(--ink); color:var(--paper); }
.btn:active { transform:translateY(1px); }
.btn:disabled { opacity:.32; cursor:not-allowed; transform:none; }
.btn:disabled:hover { background:transparent; color:var(--ink); }
.btn--acc { background:var(--acc); border-color:var(--acc); color:var(--acc-ink); }
.btn--acc:hover { background:var(--acc); border-color:var(--acc); color:var(--acc-ink); filter:brightness(1.1); }
.btn--ghost { border-color:var(--rule); color:var(--ink-2); }
.btn--ghost:hover { background:transparent; border-color:var(--ink); color:var(--ink); }
.btn--sm { min-height:36px; padding:.4rem .8rem; font-size:13px; }

/* ---------- onglets ---------- */
.tabs {
  display:inline-flex; padding:3px; gap:2px;
  border:1px solid var(--rule); border-radius:calc(var(--r) + 3px);
  background:var(--field);
}
.tab {
  appearance:none; background:none; border:0; cursor:pointer;
  padding:.5rem 1.05rem; border-radius:var(--r);
  font:500 11.5px/1 var(--mono); letter-spacing:.13em; text-transform:uppercase;
  color:var(--ink-3);
  transition:color .16s var(--ease), background .16s var(--ease);
}
.tab:hover { color:var(--ink); }
.tab[aria-selected=true] { color:var(--paper); background:var(--ink); }
@media (prefers-color-scheme:dark) { .tab[aria-selected=true] { color:var(--paper); } }
.pane[hidden] { display:none; }

/* =============================================================================
   LE SELECTEUR DE MODE
   -----------------------------------------------------------------------------
   Ce n'est pas un réglage parmi d'autres, c'est la question que pose le site :
   le fichier passe-t-il par le serveur, ou d'un appareil à l'autre ? Il occupe
   donc la largeur de la colonne, porte une icône et sa propre explication.

   Le curseur est un bloc SOUS les deux libellés, déplacé par `transform` : une
   seule couche animée, sur le compositeur, donc fluide même sur un téléphone.
   Colorer alternativement deux fonds donnerait un clignotement à chaque bascule.
   ============================================================================= */
.switch {
  position:relative; display:grid; grid-template-columns:1fr 1fr;
  width:min(400px,100%); margin:1.6rem auto 2.6rem; padding:4px;
  border:1px solid var(--rule); border-radius:99px;
  background:var(--field);
}
/* Le curseur : une pastille pleine qui glisse SOUS les deux libellés, avec une
   ombre courte pour la décoller du fond. Une seule couche animée, sur le
   compositeur, donc fluide même sur un téléphone. */
.switch .glider {
  position:absolute; top:4px; bottom:4px; left:4px; width:calc(50% - 4px);
  background:var(--ink); border-radius:99px; z-index:0;
  box-shadow:0 1px 1px rgba(0,0,0,.10), 0 6px 16px -10px rgba(0,0,0,.55);
  transition:transform .44s var(--pop);
}
body[data-mode=direct] .switch .glider { transform:translateX(100%); }

.switch .opt {
  position:relative; z-index:1; appearance:none; background:none; border:0;
  cursor:pointer; padding:.78rem 1rem; border-radius:99px;
  display:flex; align-items:center; justify-content:center; gap:.5rem;
  font:500 14.5px/1 var(--sans); color:var(--ink-3);
  transition:color .28s var(--ease);
}
.switch .opt:hover { color:var(--ink-2); }
.switch .opt svg {
  width:17px; height:17px; flex:none;
  transition:transform .44s var(--pop);
}
.switch .opt[aria-selected=true] { color:var(--paper); }
.switch .opt[aria-selected=true] svg { transform:scale(1.08); }

@media (max-width:380px) {
  .switch { width:100%; }
  .switch .opt { padding:.7rem .5rem; font-size:13.5px; gap:.4rem; }
}

/* =============================================================================
   LA DEMANDE D'ACCEPTATION (mode direct)
   -----------------------------------------------------------------------------
   Un fichier qui arrive est un évènement, pas une ligne de plus dans un
   journal : la demande s'impose au centre de l'écran et attend. Posée en bas de
   page, elle passait inaperçue et le transfert restait en suspens.

   `<dialog>` natif : l'arrière-plan est inerte, Échap ferme, le focus est piégé
   dedans — trois comportements qu'une div ne donne pas gratuitement.
   ============================================================================= */
dialog.ask {
  border:1px solid var(--rule); border-radius:calc(var(--r) + 6px);
  background:var(--paper); color:var(--ink);
  padding:0; width:min(400px,calc(100% - 2rem));
  box-shadow:var(--shadow);
}
dialog.ask::backdrop {
  background:rgba(10,9,8,.55);
  backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px);
}
dialog.ask[open] { animation:rise .34s var(--pop) both; }
dialog.ask .bd { padding:1.5rem 1.4rem 1.3rem; }
dialog.ask h3 {
  margin:0 0 .3rem; font:500 19px/1.25 var(--sans); letter-spacing:-.01em;
}
dialog.ask .from { font:400 12.5px/1 var(--mono); color:var(--ink-3); }
dialog.ask .what {
  margin:1.1rem 0 0; padding:.85rem 0 0; border-top:1px solid var(--rule-2);
  font-size:14.5px; word-break:break-word;
}
dialog.ask .sz { display:block; margin-top:.3rem; font:400 12.5px/1 var(--mono); color:var(--ink-3); }
dialog.ask .ft {
  display:grid; grid-template-columns:1fr 1fr; gap:.5rem;
  padding:0 1.4rem 1.4rem;
}
dialog.ask .ft .btn { width:100%; }

/* =============================================================================
   LA SCENE
   -----------------------------------------------------------------------------
   Une seule piece a l'ecran a la fois : la cible, la progression, ou le
   resultat. Elles se remplacent par un fondu ; rien ne se juxtapose, pour que
   l'œil n'ait jamais a chercher ou en est le transfert.
   ============================================================================= */
.stage {
  display:grid; place-items:center;
  min-height:min(62svh,560px);
  padding:1rem 0 2rem;
}
.step { width:100%; animation:rise .45s var(--pop) both; }
.step[hidden] { display:none; }
@keyframes rise {
  from { opacity:0; transform:translateY(14px) scale(.985); }
  to   { opacity:1; transform:none; }
}

/* ---------- la cible ---------- */
.target {
  position:relative; display:grid; place-items:center; gap:.85rem;
  width:100%; padding:clamp(2.8rem,10vw,4.6rem) 1.5rem; text-align:center;
  border:1px dashed var(--rule); border-radius:calc(var(--r) + 6px);
  background:var(--field); cursor:pointer; overflow:hidden;
  transition:border-color .2s var(--ease), background .2s var(--ease),
             transform .2s var(--ease);
}
.target:hover { border-color:var(--ink-3); }
.target input[type=file] { position:absolute; inset:0; opacity:0; cursor:pointer; }
.target h2 {
  margin:0; font:500 clamp(21px,5vw,30px)/1.15 var(--sans); letter-spacing:-.02em;
}
.target .sub { color:var(--ink-2); font-size:14.5px; }

/* Une flèche qui respire : le seul mouvement au repos de toute la page. */
.target .arrow {
  width:30px; height:30px; color:var(--ink-3); position:relative; z-index:1;
  animation:breathe 3.4s var(--ease) infinite;
  transition:transform .3s var(--pop), color .2s var(--ease);
}
@keyframes breathe {
  0%,100% { transform:translateY(0);    opacity:.5; }
  50%     { transform:translateY(-5px); opacity:1; }
}
.target h2, .target .sub { position:relative; z-index:1; }

/* Une lueur qui suit le curseur, allumée seulement pendant le survol d'un
   fichier. Deux variables posées par le JS, donc une seule règle ici. */
.target .halo {
  position:absolute; inset:0; opacity:0; pointer-events:none;
  background:radial-gradient(220px circle at var(--mx,50%) var(--my,50%),
             color-mix(in srgb, var(--acc) 16%, transparent), transparent 70%);
  transition:opacity .3s var(--ease);
}
body.dragging .target .halo { opacity:1; }

/* Toute la fenêtre reagit au survol d'un fichier : viser un rectangle avec dix
   fichiers dans la main est une contrainte inutile. */
body.dragging .target {
  border-color:var(--acc); border-style:solid; background:transparent;
  transform:scale(1.012);
}
body.dragging .target .arrow { animation:none; color:var(--acc); transform:translateY(-4px); }
body.dragging::after {
  content:''; position:fixed; inset:0; z-index:40; pointer-events:none;
  border:2px solid var(--acc);
  animation:flash .5s var(--ease) infinite alternate;
}
@keyframes flash { from { opacity:.25 } to { opacity:.7 } }

/* ---------- reglages, une seule ligne ---------- */
.settings {
  display:flex; gap:.5rem; flex-wrap:wrap; align-items:center;
  margin:1.1rem 0 0;
}
.chip {
  position:relative; display:inline-flex; align-items:center; gap:.45rem;
  padding:.42rem .75rem; border:1px solid var(--rule); border-radius:99px;
  font:400 12.5px/1 var(--mono); color:var(--ink-2); cursor:pointer;
  transition:border-color .16s var(--ease), color .16s var(--ease);
}
.chip:hover { border-color:var(--ink-3); color:var(--ink); }
.chip select {
  appearance:none; background:none; border:0; padding:0; margin:0;
  font:inherit; color:inherit; cursor:pointer; outline:none;
}
.chip.on { border-color:var(--acc); color:var(--acc); }
.chip .dot { width:5px; height:5px; border-radius:50%; background:currentColor; }

/* ---------- liste des pieces ---------- */
.files { list-style:none; margin:1.2rem 0 0; padding:0; }
.files li {
  display:flex; align-items:center; gap:1rem;
  padding:.62rem 0; border-bottom:1px solid var(--rule-2);
  animation:rise .3s var(--pop) both;
}
.files .nm { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.files .sz { flex:none; font:400 12.5px/1 var(--mono); color:var(--ink-3); }
.files a.nm { text-decoration:none; border-bottom:1px solid var(--rule); }
.files a.nm:hover { border-bottom-color:var(--ink); }
.files .x {
  flex:none; appearance:none; border:0; background:none; cursor:pointer;
  color:var(--ink-3); font:400 19px/1 var(--sans); padding:.2rem .35rem;
}
.files .x:hover { color:var(--acc); }
.files li.done .sz { color:var(--ok); }

/* =============================================================================
   PROGRESSION
   ============================================================================= */
.progress { display:grid; place-items:center; gap:1.4rem; text-align:center; width:100%; }
.ring { position:relative; width:min(250px,62vw); aspect-ratio:1; }
.ring > svg { width:100%; height:100%; transform:rotate(-90deg); }
.ring circle { fill:none; stroke-width:2; }
.ring .track { stroke:var(--rule); }
.ring .arc {
  stroke:var(--acc); stroke-linecap:round;
  transition:stroke-dashoffset .3s var(--ease);
}

/* Le miroitement : un pointillé qui tourne lentement par-dessus la piste.
   Il dit « ça travaille » même quand l'octet suivant se fait attendre — un
   anneau parfaitement immobile pendant dix secondes de réseau lent se lit
   comme un blocage. */
.ring .sheen {
  stroke:var(--acc); stroke-width:1; opacity:.32;
  stroke-dasharray:3 17; stroke-linecap:round;
  transform-origin:50% 50%; animation:spin 5.5s linear infinite;
}
@keyframes spin { to { transform:rotate(360deg); } }

.ring .mid { position:absolute; inset:0; display:grid; place-content:center; gap:.3rem; }
.ring .pct {
  font:500 clamp(33px,8.5vw,44px)/1 var(--mono); letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
.ring .eta { font-size:11.5px; color:var(--ink-3); letter-spacing:.04em; }

/* La coche de fin : dessinée d'un trait, une fois. */
.ring .tick {
  position:absolute; inset:0; margin:auto; width:34%; height:34%;
  color:var(--ok); opacity:0; pointer-events:none;
  stroke-dasharray:44; stroke-dashoffset:44;
}
.ring.full .arc   { stroke:var(--ok); }
.ring.full .sheen { animation:none; opacity:0; }
.ring.full .mid   { animation:fade .3s var(--ease) forwards; }
.ring.full .tick  { opacity:1; animation:draw .5s var(--pop) .12s forwards; }
@keyframes fade { to { opacity:0; transform:scale(.9); } }
@keyframes draw { to { stroke-dashoffset:0; } }

.progress .what { max-width:34ch; }
.progress .rate { font:400 12.5px/1 var(--mono); color:var(--ink-3); }
.progress .files { width:min(460px,100%); margin-top:0; }

/* =============================================================================
   RESULTAT — le code QR est la piece maitresse
   ============================================================================= */
.done { display:grid; gap:1.6rem; justify-items:center; text-align:center; }
.done h2 { margin:0; font:500 clamp(22px,5vw,27px)/1.2 var(--sans); letter-spacing:-.02em; }

.qr {
  padding:1.1rem; background:var(--paper); color:var(--ink);
  border:1px solid var(--rule); border-radius:calc(var(--r) + 4px);
  box-shadow:var(--shadow);
  width:min(268px,66vw); animation:pop .5s var(--pop) both;
}
.qr svg { width:100%; height:auto; }
@keyframes pop {
  from { opacity:0; transform:scale(.9) rotate(-1.5deg); }
  to   { opacity:1; transform:none; }
}
.qr-hint { font:400 12.5px/1.5 var(--mono); color:var(--ink-3); max-width:30ch; }

.copyline { display:flex; gap:.45rem; width:min(520px,100%); }
.copyline input {
  flex:1 1 auto; min-width:0; min-height:46px; padding:.6rem .8rem;
  font-family:var(--mono); font-size:13px;
  background:var(--field); color:var(--ink-2);
  border:1px solid var(--rule); border-radius:var(--r); outline:none;
  text-overflow:ellipsis;
}

.codebox {
  display:flex; align-items:center; gap:1rem; flex-wrap:wrap; justify-content:center;
  padding:.9rem 1.2rem; border:1px solid var(--acc); border-radius:var(--r);
}
.codebox .digits {
  font:500 clamp(25px,7vw,32px)/1 var(--mono); letter-spacing:.3em;
  color:var(--acc); padding-left:.3em;      /* compense l'interlettrage final */
}
.acts { display:flex; gap:.5rem; flex-wrap:wrap; justify-content:center; }

/* =============================================================================
   SAISIE DU CODE — page de retrait
   ============================================================================= */
.codein { display:flex; gap:.5rem; justify-content:center; margin:0 0 1.4rem; }
.codein input {
  width:clamp(42px,13vw,56px); height:clamp(58px,16vw,70px); padding:0;
  text-align:center; font:500 clamp(23px,6vw,27px)/1 var(--mono);
  background:var(--field); border:1px solid var(--rule); border-radius:var(--r);
  outline:none; transition:border-color .15s var(--ease);
}
.codein input:focus { border-color:var(--acc); }
.codein.bad input { border-color:var(--acc); animation:shake .32s var(--ease); }
@keyframes shake { 25%{transform:translateX(-5px)} 75%{transform:translateX(5px)} }

/* =============================================================================
   MODE DIRECT
   ============================================================================= */
.peers { list-style:none; margin:1.2rem 0 0; padding:0; display:grid; gap:.55rem; }
.peer {
  display:flex; align-items:center; gap:1rem; width:100%; text-align:left;
  padding:1.05rem 1.15rem; border:1px solid var(--rule); border-radius:var(--r);
  background:transparent; cursor:pointer;
  animation:rise .35s var(--pop) both;
  transition:border-color .16s var(--ease), transform .16s var(--ease);
}
.peer:hover { border-color:var(--ink); }
.peer:active { transform:scale(.994); }
.peer .dot {
  flex:none; width:8px; height:8px; border-radius:50%; background:var(--ok);
  position:relative;
}
.peer .dot::after {
  content:''; position:absolute; inset:-4px; border-radius:50%;
  border:1px solid var(--ok); opacity:0; animation:sonar 2.6s var(--ease) infinite;
}
@keyframes sonar {
  0%   { transform:scale(.6); opacity:.9; }
  70%  { transform:scale(1.9); opacity:0; }
  100% { opacity:0; }
}
.peer .nm { flex:1 1 auto; font-weight:500; }
.peer .go { flex:none; font:400 12.5px/1 var(--mono); color:var(--ink-3); }

.empty {
  padding:2.4rem 1rem; text-align:center; color:var(--ink-3);
  border:1px dashed var(--rule); border-radius:var(--r);
  margin-top:1.2rem;
}

/* Un radar qui cherche : trois cercles qui s'ouvrent, décalés. Sans lui, une
   liste vide se lit comme une panne plutôt que comme une attente. */
.radar { position:relative; display:block; width:56px; height:56px; margin:0 auto 1rem; }
.radar i {
  position:absolute; inset:0; margin:auto; width:10px; height:10px;
  border:1px solid var(--ink-3); border-radius:50%;
  animation:ping 2.8s var(--ease) infinite;
}
.radar i:nth-child(2) { animation-delay:.9s; }
.radar i:nth-child(3) { animation-delay:1.8s; }
@keyframes ping {
  0%   { transform:scale(.5); opacity:.9; }
  80%  { transform:scale(5.2); opacity:0; }
  100% { opacity:0; }
}

/* =============================================================================
   VOS ENVOIS RECENTS
   -----------------------------------------------------------------------------
   Il n'y a pas de compte : sans cette liste, un lien qu'on n'a pas collé tout
   de suite est perdu pour de bon. Elle vit dans le navigateur, pas au serveur.
   ============================================================================= */
#recent { margin-top:2.4rem; animation:rise .4s var(--pop) both; }
.recent-hd { display:flex; align-items:baseline; gap:1rem; margin-bottom:.3rem; }
.lnk {
  appearance:none; background:none; border:0; padding:0; cursor:pointer;
  font:400 11px/1 var(--mono); letter-spacing:.1em; text-transform:uppercase;
  color:var(--ink-3); border-bottom:1px solid transparent;
}
.lnk:hover { color:var(--acc); border-bottom-color:currentColor; }

.recent-list { list-style:none; margin:0; padding:0; }
.recent-list li {
  display:flex; align-items:center; gap:.8rem;
  padding:.72rem 0; border-bottom:1px solid var(--rule-2);
}
.recent-list .nm {
  flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; font-size:14.5px;
}
.recent-list .when { flex:none; font:400 11.5px/1 var(--mono); color:var(--ink-3); }
.recent-list .cd {
  flex:none; font:500 12px/1 var(--mono); letter-spacing:.08em; color:var(--acc);
}
.recent-list .btn { flex:none; }

/* =============================================================================
   INVITER UN APPAREIL
   -----------------------------------------------------------------------------
   Deux gestes, pas un : on ouvre un salon, ou on rejoint celui de quelqu'un.
   Les mettre côte à côte évite la question « et moi, je fais quoi ? » de celui
   qui reçoit le code.
   ============================================================================= */
.invite-hd { display:flex; align-items:center; gap:.8rem; margin-bottom:.25rem; }
.invite-hd h3 { margin:0; }

.invite-two {
  display:grid; grid-template-columns:1fr auto 1fr; gap:1.2rem; align-items:center;
  margin-top:1.1rem;
}
/* Un filet vertical entre les deux gestes, avec « ou » posé dessus. */
.invite-two .or {
  display:grid; place-items:center; gap:.5rem; align-self:stretch;
  font:400 10.5px/1 var(--mono); letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink-3);
}
.invite-two .or::before,
.invite-two .or::after { content:''; width:1px; flex:1 1 auto; background:var(--rule); }
.invite-two .or::before { min-height:14px; }
.invite-two .or::after  { min-height:14px; }
.invite-two .half { display:grid; gap:.55rem; }
.invite-two .half .btn { width:100%; }

/* Le champ de code : quatre lettres, écrites grand et espacées, parce qu'on les
   lit à voix haute ou on les recopie depuis un autre écran. */
.joinbox { display:flex; gap:.45rem; }
.joinbox input {
  flex:1 1 auto; min-width:0; min-height:46px; padding:.5rem .7rem;
  text-align:center; text-transform:uppercase;
  font:500 19px/1 var(--mono); letter-spacing:.3em; text-indent:.3em;
  background:var(--field); color:var(--ink);
  border:1px solid var(--rule); border-radius:var(--r); outline:none;
  transition:border-color .16s var(--ease);
}
.joinbox input:focus { border-color:var(--acc); }
.joinbox input::placeholder { color:var(--ink-3); letter-spacing:.3em; }
.joinbox.bad input { border-color:var(--acc); animation:shake .32s var(--ease); }

/* ---------- le salon ouvert ---------- */
.room { display:grid; gap:1.3rem; justify-items:center; text-align:center; margin-top:1.2rem; }
.room .qr { animation:pop .5s var(--pop) both; }

.roomcode {
  font:500 clamp(30px,9vw,40px)/1 var(--mono); letter-spacing:.34em;
  padding-left:.34em; color:var(--acc);
}

/* Le compte à rebours : discret, puis rouge dans les cinq dernières minutes. */
.countdown {
  display:inline-flex; align-items:center; gap:.45rem;
  font:400 11.5px/1 var(--mono); color:var(--ink-3);
}
.countdown::before {
  content:''; width:6px; height:6px; border-radius:50%;
  border:1px solid currentColor;
}
.countdown.soon { color:var(--acc); }

/* L'état d'attente. Un point qui bat vaut mieux qu'une phrase figée : il dit
   que la page écoute encore. */
.waiting {
  display:inline-flex; align-items:center; gap:.55rem;
  font:400 13px/1 var(--sans); color:var(--ink-2);
}
.waiting .pulse {
  width:7px; height:7px; border-radius:50%; background:var(--ink-3);
  animation:beat2 1.8s var(--ease) infinite;
}
.waiting.here { color:var(--ok); }
.waiting.here .pulse { background:currentColor; animation:none; }
@keyframes beat2 { 0%,100% { opacity:.25; transform:scale(.85) } 50% { opacity:1; transform:scale(1) } }

@media (max-width:520px) {
  /* Les deux gestes s'empilent, et le filet devient horizontal. */
  .invite-two { grid-template-columns:1fr; gap:.9rem; }
  .invite-two .or { flex-direction:row; padding:.1rem 0; }
  .invite-two .or::before,
  .invite-two .or::after { width:auto; height:1px; min-height:0; align-self:center; }
  .invite-two .or { display:flex; align-items:center; gap:.7rem; }
}

/* =============================================================================
   LE LECTEUR DE CODE QR
   -----------------------------------------------------------------------------
   Safari sur iPhone n'a pas de décodeur natif : le décodage se fait en
   JavaScript, sur les images de la caméra. La fenêtre est donc volontairement
   sombre et pleine — on tient le téléphone à bout de bras, il faut voir grand.
   ============================================================================= */
dialog.scan {
  border:0; padding:0; overflow:hidden;
  width:min(440px,calc(100% - 1.4rem));
  border-radius:calc(var(--r) + 8px);
  background:#0b0a0a; color:#f2f0ec;
  box-shadow:var(--shadow);
}
dialog.scan::backdrop { background:rgba(6,5,5,.82); }
dialog.scan[open] { animation:rise .3s var(--pop) both; }

.scan-stage { position:relative; aspect-ratio:1; background:#000; }
.scan-stage video { width:100%; height:100%; object-fit:cover; display:block; }

/* Le cadre de visée : un rectangle clair, le reste assombri par une ombre
   démesurée — plus simple qu'un masque, et compatible partout. */
.scan-frame {
  position:absolute; inset:15%; border-radius:14px;
  border:2px solid rgba(242,240,236,.85);
  box-shadow:0 0 0 100vmax rgba(0,0,0,.45);
}
.scan-frame::after {
  content:''; position:absolute; left:5%; right:5%; top:4%; height:2px;
  background:var(--acc); box-shadow:0 0 14px var(--acc);
  animation:sweep 2.6s var(--ease) infinite;
}
@keyframes sweep { 0%,100% { top:4% } 50% { top:93% } }

.scan-ft {
  display:flex; align-items:center; gap:1rem; flex-wrap:wrap;
  padding:1rem 1.1rem calc(1rem + env(safe-area-inset-bottom));
}
.scan-hint { flex:1 1 12ch; margin:0; font:400 13px/1.4 var(--sans); color:rgba(242,240,236,.66); }
.scan-hint.bad { color:var(--acc); }
.scan-ft .btn { border-color:rgba(242,240,236,.3); color:#f2f0ec; }
.scan-ft .btn:hover { background:#f2f0ec; color:#0b0a0a; }

/* ---------- cartes ---------- */
.card {
  border:1px solid var(--rule); border-radius:var(--r);
  padding:1.3rem; margin:1.3rem 0;
  animation:rise .35s var(--pop) both;
}
.card h3 { margin:0 0 .7rem; font:500 16px/1.3 var(--sans); }

.bar { position:relative; height:2px; background:var(--rule-2); overflow:hidden; }
.bar > i {
  position:absolute; inset:0 auto 0 0; width:0; background:var(--acc);
  transition:width .25s linear;
}

.pill {
  display:inline-flex; align-items:center; gap:.4rem; white-space:nowrap;
  font:500 10px/1 var(--mono); letter-spacing:.11em; text-transform:uppercase;
  padding:.32rem .5rem; border:1px solid var(--rule); border-radius:99px;
  color:var(--ink-2);
}
.pill::before { content:''; width:5px; height:5px; border-radius:50%; background:currentColor; }
.pill--live { color:var(--ok); border-color:currentColor; }
.pill--dead { color:var(--ink-3); }
.pill--warn { color:var(--acc); border-color:currentColor; }

/* ---------- messages ---------- */
.note {
  padding:.8rem 1rem; border-left:2px solid var(--acc);
  background:var(--field); font-size:14px; margin:1.1rem 0;
  border-radius:0 var(--r) var(--r) 0;
}
.note--ok { border-left-color:var(--ok); }

.toast {
  position:fixed; left:50%; bottom:calc(1.6rem + env(safe-area-inset-bottom));
  transform:translate(-50%,150%);
  padding:.72rem 1.15rem; border-radius:99px;
  background:var(--ink); color:var(--paper);
  font:400 14px/1 var(--sans); z-index:80; pointer-events:none;
  box-shadow:var(--shadow);
  transition:transform .3s var(--pop);
}
.toast.on { transform:translate(-50%,0); }

/* ---------- pied ---------- */
.foot {
  margin:3rem 0 2rem; padding-top:1.1rem; border-top:1px solid var(--rule);
  display:flex; gap:1rem; flex-wrap:wrap; align-items:baseline;
  font:400 11.5px/1.6 var(--mono); color:var(--ink-3);
}
.foot a { text-decoration:none; border-bottom:1px solid var(--rule); }
.foot a:hover { color:var(--ink); border-bottom-color:var(--ink); }

/* =============================================================================
   PANNEAU D'USAGE (/admin)
   ============================================================================= */
.grid {
  display:grid; gap:.7rem; margin:1.4rem 0;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
}
.stat {
  padding:1rem 1.1rem; border:1px solid var(--rule); border-radius:var(--r);
  animation:rise .35s var(--pop) both;
}
.stat .n {
  display:block; font:500 clamp(24px,4vw,30px)/1 var(--mono);
  letter-spacing:-.02em; margin-bottom:.4rem;
}
.stat .n small { font-size:.5em; color:var(--ink-3); letter-spacing:0; }
.stat.hot .n { color:var(--acc); }

/* Histogramme des trente derniers jours, en CSS pur. */
.spark { display:flex; align-items:flex-end; gap:2px; height:64px; margin:.4rem 0 0; }
.spark i {
  flex:1 1 0; min-width:2px; background:var(--ink-3); border-radius:1px;
  transition:background .16s var(--ease);
  animation:grow .5s var(--pop) both;
}
.spark i.acc { background:var(--acc); }
.spark i:hover { background:var(--ink); }
@keyframes grow { from { height:0 !important; opacity:0 } }

.feed { list-style:none; margin:0; padding:0; }
.feed li {
  display:flex; align-items:baseline; gap:.75rem; flex-wrap:wrap;
  padding:.6rem 0; border-bottom:1px solid var(--rule-2);
  font-size:14px;
}
.feed .when { font:400 11.5px/1 var(--mono); color:var(--ink-3); flex:none; width:9.5em; }
.feed .who  { font:400 11.5px/1 var(--mono); color:var(--ink-3); }
.feed .what { flex:1 1 12em; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

.tr { border-bottom:1px solid var(--rule-2); padding:.95rem 0; }
.tr:last-child { border-bottom:0; }
.tr .hd { display:flex; align-items:baseline; gap:.7rem; flex-wrap:wrap; }
.tr .ttl { font:500 15px/1.3 var(--sans); flex:1 1 12em; min-width:0;
           overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tr .sub { margin-top:.25rem; font:400 12px/1.6 var(--mono); color:var(--ink-3); }
.tr .acts { justify-content:flex-start; margin-top:.6rem; }

.gauge { display:flex; align-items:center; gap:.6rem; }
.gauge .track { flex:1 1 auto; height:3px; background:var(--rule); position:relative; }
.gauge .track > i { position:absolute; inset:0 auto 0 0; background:var(--ink-2); transition:width .4s var(--ease); }
.gauge.hot .track > i { background:var(--acc); }

/* ---------- entree admin ---------- */
.gate { display:grid; place-items:center; min-height:64svh; }
.gate form { width:min(340px,100%); }
.gate h2 { margin:0 0 .3rem; font:500 20px/1.25 var(--sans); }
.gate input {
  width:100%; min-height:46px; padding:.6rem .8rem; margin:1rem 0;
  background:var(--field); color:var(--ink);
  border:1px solid var(--rule); border-radius:var(--r); outline:none;
}
.gate input:focus { border-color:var(--ink); }

/* =============================================================================
   ÉTROIT
   -----------------------------------------------------------------------------
   Le service sert surtout depuis un téléphone : on envoie une vidéo qu'on vient
   de tourner, on scanne un code. Les règles ci-dessous descendent jusqu'à
   320 px de large — la plus petite fenêtre qu'on rencontre encore.
   ============================================================================= */
@media (max-width:600px) {
  :root { --col:min(980px,100% - 1.6rem); }

  .top { padding:1.2rem 0 .9rem; gap:.6rem; }
  .top #limitNote { width:100%; }          /* passe à la ligne plutôt que de serrer le titre */

  .stage { min-height:auto; padding:.5rem 0 1.5rem; }
  .card { padding:1rem; }

  /* Le champ et son bouton s'empilent : côte à côte, le lien devient illisible
     et le bouton se réduit à deux lettres. */
  .copyline { flex-wrap:wrap; }
  .copyline input { flex:1 1 100%; }
  .copyline .btn { flex:1 1 auto; }

  .codebox { justify-content:space-between; padding:.8rem 1rem; }
  .acts { width:100%; }
  .acts .btn { flex:1 1 auto; }

  .recent-list li { flex-wrap:wrap; row-gap:.35rem; }
  .recent-list .nm { flex:1 1 100%; }

  .feed li { gap:.4rem .7rem; }
  .feed .when { width:100%; }
  .feed .what { flex:1 1 100%; white-space:normal; }

  .grid { grid-template-columns:repeat(auto-fit,minmax(128px,1fr)); gap:.5rem; }
  .stat { padding:.8rem .9rem; }
  .spark { height:48px; }

  .tr .acts .btn { flex:0 1 auto; }
}

@media (max-width:360px) {
  .qr { width:min(240px,78vw); padding:.8rem; }
  .codein { gap:.35rem; }
  .codein input { width:clamp(38px,13.5vw,48px); }
  .codebox .digits { letter-spacing:.22em; padding-left:.22em; }
  .chip { font-size:11.5px; padding:.38rem .6rem; }
  dialog.ask .ft { grid-template-columns:1fr; }
}

@media (prefers-reduced-motion:reduce) {
  *,*::before,*::after { animation:none !important; transition:none !important; }
}
