/* Movimento condiviso dell'app (tutte le pagine del layout nuovo, shared/head.php, e il login,
   master/head.php). Stesse tre curve della dashboard (css/dashboard.css, --sd2-*) e del
   Calendar (globals.css, --mv-*): il ritmo e' lo stesso ovunque.
   Regola: il movimento dice cosa e' successo (un tocco, un cambio di stato, qualcosa che
   arriva o se ne va), mai decorazione. Arrivi distesi, uscite piu' brevi delle entrate.
   Le entrate usano "backwards": finita l'animazione non resta nessun effetto (niente
   contesti di impilamento permanenti sotto le finestre fisse). */

:root {
    --mv-arrivo: cubic-bezier(.16, 1, .3, 1);   /* risposta a un tocco: parte subito e si posa */
    --mv-morbida: cubic-bezier(.45, 0, .2, 1);  /* cose che arrivano o si riempiono */
    --mv-uscita: cubic-bezier(.4, 0, 1, 1);     /* cose che se ne vanno */
}

/* Dissolvenza: contenuto che arriva dopo un caricamento, testo che cambia. */
.mv-sfuma { animation: mvSfuma .42s var(--mv-morbida) backwards; }
.mv-sfuma.mv-esce { animation: mvSvanisce .24s var(--mv-uscita) both; }
@keyframes mvSfuma { from { opacity: 0; } }
@keyframes mvSvanisce { to { opacity: 0; } }

/* Arrivo dal basso: un messaggio nuovo in chat, un file appena caricato. */
.mv-sale { animation: mvSale .5s var(--mv-arrivo) backwards; }
@keyframes mvSale { from { opacity: 0; transform: translateY(10px); } }

/* Blocco che si apre in altezza. Struttura: .mv-apri > (un solo figlio). */
.mv-apri { display: grid; grid-template-rows: 1fr; animation: mvApri .42s var(--mv-arrivo) backwards; }
.mv-apri > * { min-height: 0; overflow: hidden; }
@keyframes mvApri { from { grid-template-rows: 0fr; opacity: 0; } }

/* Barra che cresce fino al suo valore la prima volta che la pagina si apre. */
.mv-cresce { transform-origin: left center; animation: mvCresce .9s var(--mv-morbida) .15s backwards; }
@keyframes mvCresce { from { transform: scaleX(0); } }

/* Piccola scossa su un errore (come il questionario). */
.mv-scossa { animation: mvScossa .34s var(--mv-arrivo); }
@keyframes mvScossa {
    0%, 100% { transform: none; }
    20% { transform: translateX(-6px); }
    45% { transform: translateX(5px); }
    70% { transform: translateX(-3px); }
}

/* Una notifica non ancora letta: parte con un azzurro piu deciso che sfuma lentamente nel fondo della card, cosi si
   vede quali sono nuove anche se l'apertura le segna gia' come lette. */
.mv-nuova { animation: mvNuova 2.4s var(--mv-morbida) .4s backwards; }
@keyframes mvNuova { from { background-color: var(--c-c2); } }

/* Spunta che si disegna (fine caricamento). */
.mv-spunta { display: inline-block; width: 16px; height: 16px; vertical-align: -3px; }
.mv-spunta circle { transform-origin: center; animation: mvPop .45s var(--mv-arrivo) backwards; }
.mv-spunta path { stroke-dasharray: 20; animation: mvTraccia .45s var(--mv-morbida) .2s backwards; }
@keyframes mvPop { 0% { transform: scale(.5); opacity: 0; } 60% { transform: scale(1.12); opacity: 1; } 100% { transform: none; } }
@keyframes mvTraccia { from { stroke-dashoffset: 20; } to { stroke-dashoffset: 0; } }

/* Finestra sovrapposta: velo che sfuma, foglio che sale (telefono) o appare (desktop). */
.mv-velo { animation: mvSfuma .32s var(--mv-morbida) backwards; }
.mv-velo.mv-esce { animation: mvSvanisce .22s var(--mv-uscita) both; }
.mv-foglio { animation: mvFoglio .48s var(--mv-arrivo) backwards; }
.mv-foglio.mv-esce { animation: mvFoglioEsce .22s var(--mv-uscita) both; }
@keyframes mvFoglio { from { opacity: 0; transform: translateY(40px); } }
@keyframes mvFoglioEsce { to { opacity: 0; transform: translateY(24px); } }
@media only screen and (min-width: 1023px) {
    .mv-foglio { animation-name: mvFinestra; }
    .mv-foglio.mv-esce { animation-name: mvFinestraEsce; }
}
@keyframes mvFinestra { from { opacity: 0; transform: translateY(10px) scale(.97); } }
@keyframes mvFinestraEsce { to { opacity: 0; transform: scale(.98); } }

/* Conversazione della chat (e canale di classe): sul telefono entra da destra come una
   pagina nuova, su desktop appare in dissolvenza; esce allo stesso modo, piu' in fretta. */
.chat-detail.mv-entra, .canale-detail.mv-entra { animation: mvChatEntra .44s var(--mv-arrivo) backwards; }
.chat-detail.mv-esce, .canale-detail.mv-esce { animation: mvChatEsce .22s var(--mv-uscita) both; pointer-events: none; }
@keyframes mvChatEntra { from { opacity: 0; transform: translateX(40px); } }
@keyframes mvChatEsce { to { opacity: 0; transform: translateX(28px); } }
@media only screen and (min-width: 1023px) {
    .chat-detail.mv-entra, .canale-detail.mv-entra { animation-name: mvSfuma; }
    .chat-detail.mv-esce, .canale-detail.mv-esce { animation-name: mvSvanisce; }
}
/* Anteprima a schermo intero delle immagini della chat. */
.chat-lightbox { animation: mvSfuma .3s var(--mv-morbida) backwards; }
.chat-lightbox img { animation: mvFinestra .42s var(--mv-arrivo) backwards; }
.chat-lightbox.mv-esce { animation: mvSvanisce .2s var(--mv-uscita) both; }

/* Finestre .modal dell'app (Job & Stage, accettazione dei regolamenti). La prima si apre
   con la transizione di desktop.css/mobile.css; l'accettazione arriva gia' aperta dal
   server (e deve restarlo anche senza JavaScript), quindi entra con un'animazione. */
.regulation-ack-modal .modal-overlay { animation: mvSfuma .4s var(--mv-morbida) backwards; }
.regulation-ack-modal .modal-wrapper { animation: mvSfuma .5s var(--mv-morbida) .12s backwards; }
.regulation-ack-modal .modal-body { animation: mvSale .6s var(--mv-arrivo) .2s backwards; }
/* Barra di avanzamento dell'invio della candidatura: scorre invece di scattare. */
.progress-completed { transition: width .35s var(--mv-arrivo); }

/* Caricamento di file (dispense, esami, contest): il riquadro entra dal basso, la barra
   scorre invece di scattare e la percentuale non lampeggia piu' (il numero che cresce
   basta a dire che sta andando). Stessa specificita' di desktop.css, che viene prima. */
.loader-box { animation: mvSale .42s var(--mv-arrivo) backwards; }
.loader-box .loader .perc, .files .loader-box .loader .perc { animation: none; }
.loader-box .loader .bar, .files .loader-box .loader .bar { transition: width .35s var(--mv-arrivo); }

/* Moduli e pannelli che si aprono a comando (nuova dispensa, nuovo esame, consegne): ogni
   volta che passano da nascosti a visibili arrivano dal basso invece di comparire. */
.add-dispensa, .add-esame, .consegne-content { animation: mvSale .42s var(--mv-arrivo) backwards; }

/* Accesso e password (login.php, recupera_pwd.php, cambia_pwd.php): mentre il modulo
   parte, il pulsante pulsa piano e non si puo' premere due volte. */
#login form.in-invio button { pointer-events: none; animation: mvAttesa 1s ease-in-out infinite; }
@keyframes mvAttesa { 50% { opacity: .45; } }

/* Fatture: "causale copiata" cambia colore con una transizione anche sul telefono (su
   desktop c'era gia'), e il testo nuovo arriva in dissolvenza. */
.fattura-rate .rate-caus { transition: background-color .25s var(--mv-morbida), color .25s var(--mv-morbida), border-color .25s var(--mv-morbida); }
.fattura-rate .rate-caus.ok .cs { animation: mvSfuma .3s var(--mv-morbida) backwards; }

/* Caricamento non riuscito (js/avviso.js, caricamentoFallito): testo e barra in rosso. */
.loader-box.loader-errore .loader .perc { color: #8a231a; font-weight: 700; }
.loader-box.loader-errore .loader .bar { background-color: #fbe3e0; }

/* Indicatore delle operazioni in corso di js/controller.js ($("#loading")): nel layout
   nuovo l'elemento non esisteva e i salvataggi non mostravano nulla. Lo crea avviso.js:
   una barra sottile in cima alla pagina. */
#loading.mv-caricamento {
    display: none; position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 10001;
    overflow: hidden; background: rgba(4, 235, 216, .22); pointer-events: none;
}
#loading.mv-caricamento::after {
    content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 35%;
    background: var(--c-v0); border-radius: 3px; animation: mvScorre 1.1s var(--mv-morbida) infinite; /* acqua: si vede sia sull'intestazione blu sia sul bianco */
}
@keyframes mvScorre { from { transform: translateX(-100%); } to { transform: translateX(290%); } }

/* Errori dei moduli (js/form.js, pagine esami del docente): nel layout nuovo il riquadro
   non aveva stile e si confondeva col resto. */
.error-box {
    margin-top: 12px; padding: 10px 14px; border-radius: 10px; background: #fbe3e0; color: #8a231a;
    font-size: 13px; line-height: 1.45; animation: mvScossa .34s var(--mv-arrivo);
}

/* ——— Avvisi (js/avviso.js): al posto degli alert() del browser ———
   Salgono dal basso, sopra la barra del menu sul telefono, in basso a destra su desktop.
   Gli esiti positivi se ne vanno da soli; gli errori restano finche' non li chiudi (o
   per un tempo lungo), perche' vanno letti. */
.mv-avvisi {
    position: fixed; z-index: 10000; left: 12px; right: 12px; bottom: calc(118px + env(safe-area-inset-bottom, 0px));
    display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none;
}
@media only screen and (min-width: 1023px) {
    .mv-avvisi { left: auto; right: 28px; bottom: 28px; align-items: flex-end; }
}
.mv-avviso {
    pointer-events: auto; display: flex; align-items: flex-start; gap: 10px;
    width: 100%; max-width: 420px; padding: 13px 14px 13px 16px; border-radius: 14px;
    background: #fff; color: #14141a; box-shadow: 0 10px 30px rgba(11, 11, 20, .16), 0 2px 6px rgba(11, 11, 20, .08);
    font: 14px/1.4 "hk_superregular", -apple-system, "Segoe UI", Roboto, sans-serif;
    animation: mvAvviso .5s var(--mv-arrivo) backwards;
}
.mv-avviso.mv-esce { animation: mvAvvisoEsce .24s var(--mv-uscita) both; }
@keyframes mvAvviso { from { opacity: 0; transform: translateY(16px) scale(.98); } }
@keyframes mvAvvisoEsce { to { opacity: 0; transform: translateY(8px); } }
.mv-avviso-icona { flex-shrink: 0; width: 20px; height: 20px; margin-top: 1px; }
.mv-avviso-testo { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.mv-avviso-chiudi {
    flex-shrink: 0; width: 26px; height: 26px; margin: -3px -4px -3px 0; border: 0; border-radius: 50%;
    background: transparent; color: #63636b; cursor: pointer; display: flex; align-items: center; justify-content: center;
    transition: background-color .15s ease;
}
.mv-avviso-chiudi:hover { background: #f1f2f7; }
.mv-avviso-chiudi:focus-visible { outline: 2px solid var(--c-c0); outline-offset: 1px; }
.mv-avviso--errore { background: #fff5f4; color: #8a231a; }
.mv-avviso--errore .mv-avviso-chiudi { color: #8a231a; }
.mv-avviso--errore .mv-avviso-chiudi:hover { background: #fbe3e0; }

/* Chi chiede meno movimento: niente spostamenti, solo dissolvenze brevi. */
@media (prefers-reduced-motion: reduce) {
    .mv-sale, .mv-apri, .mv-foglio, .mv-avviso, .loader-box, .add-dispensa, .add-esame, .consegne-content, .chat-detail.mv-entra, .canale-detail.mv-entra, .chat-lightbox img { animation-name: mvSfuma; animation-duration: .15s; }
    .mv-foglio.mv-esce, .mv-avviso.mv-esce, .chat-detail.mv-esce, .canale-detail.mv-esce { animation-name: mvSvanisce; }
    #login form.in-invio button { animation: none; opacity: .6; }
    #loading.mv-caricamento::after { animation: none; width: 100%; opacity: .5; }
    .error-box { animation: none; }
    .mv-cresce, .mv-scossa, .mv-nuova, .mv-spunta circle, .mv-spunta path, .regulation-ack-modal .modal-body { animation: none; }
    /* le finestre .modal: solo la dissolvenza, niente spostamento */
    .modal-transition { transition-property: opacity !important; }
}
