/* =============================================================================
   chat-patch2.css — patch 2 pentru chat. Se încarcă DUPĂ `chat-v7.css`.
   -----------------------------------------------------------------------------
   Nu înlocuiește nimic: doar suprascrie punctual. Dacă îl scoți, revii exact la
   comportamentul de acum. Fiecare bloc spune ce repară și cum a fost măsurat.

   Reproducerea defectelor: `masuratori/` (randează ruta reală de chat cu twig-ul
   și CSS-ul din repo, fără bază de date).
   ============================================================================= */


/* =========================================================================
   1. GRANULAȚIA — „texturile ciudate" și „cutia de sub Andu AI"
   -------------------------------------------------------------------------
   Sunt același defect. `body::after` (chat-v7.css:146) desenează o granulație
   de zgomot fractal cu `background-size: 140px` la `opacity: .04`.

   Pe landing, peste imagini și carduri, dala de 140px se pierde. Pe chat, peste
   suprafețe mari și foarte întunecate, se întâmplă invers: dala se repetă de
   ~2,8 ori pe un ecran de 390px, iar structura ei la scară mare devine
   VIZIBILĂ — se citește ca dreptunghiuri cu cusătură între ele, nu ca textură.
   Fiind `position: fixed`, nu derulează, deci pare lipită de ecran: exact
   „cutia" care rămâne și după ce dai „Sunt de acord".

   Reparația păstrează intenția (textură premium) și scoate artefactul: dală mult
   mai mică și zgomot mai fin, deci nu se mai poate organiza în blocuri.
   `baseFrequency` urcă de la .8 la 1.6 — zgomot de două ori mai fin.
   ====================================================================== */
body::after {
  background-size: 4rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64'%3E%3Cfilter id='n2'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.6' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='64' height='64' filter='url(%23n2)' opacity='1'/%3E%3C/svg%3E");
}
/* Pe ecrane mici suprafețele plate sunt proporțional mai mari, deci și cel mai
   mic zgomot se vede mai bine. 3% în loc de 4%. */
@media (max-width: 719.98px) {
  body::after { opacity: .03; }
}
/* Ecranele cu densitate mare afișează dala la jumătate de dimensiune fizică;
   fără asta granulația devine aproape invizibilă și nu mai face nimic. */
@media (min-resolution: 2dppx) {
  body::after { background-size: 3rem; }
}


/* =========================================================================
   2. DISCLAIMERUL — REVENIT LA MECANISMUL v7 (1:1 cu replica)
   -------------------------------------------------------------------------
   Aici stătea rescrierea din patch2 (derulare pe CARD, înălțime `--dz-h` scrisă
   din JS de la capul listei până la buton, bară de scroll vizibilă). MĂSURAT pe
   1920×900, ea producea trei devieri față de replică:
     · caseta 664px înaltă, în loc de 504px;
     · pagina primea 57px de scroll inutil, deși totul încape pe ecran;
     · bara de scroll era vizibilă, iar v7 nu are niciuna.

   v7 (replica-noua/assets/css/chat-v6.css:383-392) face altfel, și e ce se
   restaurează aici: derulează CONTAINERUL, plafonat la `min(56vh, 560px)`
   (`min(48vh, 420px)` sub 720px), FĂRĂ bară de scroll — afordanța e o mască de
   estompare de 22px jos, iar padding-ul de sub text (34px) o depășește, deci
   ultimul rând nu e niciodată voalat.

   Din înălțimea mai mică dispare și scrollul de pagină: e o consecință, nu o
   reparație separată.

   `--dz-h` rămâne scris de `chat-patch2.js`, dar nimeni nu-l mai citește — inert.
   ====================================================================== */
/* -------------------------------------------------------------------------
   CORECȚIE (raportat pe telefon): caseta „se termina brusc", iar rama se vedea
   doar la capătul de sus sau la cel de jos al derulării.

   CAUZA, una singură pentru amândouă: rama, fundalul ȘI derularea stăteau pe
   elemente greșite.
     · Fundalul + rama în gradient erau pe CARD (`.message-assistant-disclaimer`),
       iar cardul e conținutul din interiorul zonei care derulează. Rama fiind
       desenată cu `::before { position:absolute; inset:0 }`, se întindea pe toată
       ÎNĂLȚIMEA DE CONȚINUT și derula odată cu textul: latura de sus se vedea doar
       la scroll 0, cea de jos doar la capăt. Exact ce se raportează.
     · Masca de estompare era pe ACELAȘI element care purta fundalul, deci nu
       estompa doar textul — stingea și fundalul, și rama. De aici senzația de
       tăiat brusc: jos nu mai exista nicio muchie, textul se pierdea în pagină.

   REPARAȚIA inversează responsabilitățile:
     · CONTAINERUL devine rama — nu derulează, deci fundalul, colțurile și chenarul
       în gradient stau pe loc, mereu vizibile, ca un cadru.
     · CARDUL dinăuntru devine zona care derulează și poartă el masca. Estomparea
       cade acum peste fundalul cadrului, nu peste pagină, deci textul se stinge
       ÎN interiorul casetei, iar muchia de jos rămâne desenată.
   Plafoanele de înălțime și absența barei de scroll rămân cele din v7.
   ---------------------------------------------------------------------- */
.up5-chat .message-container:has(.message-assistant-disclaimer) {
  position: relative;
  max-width: 100%;
  display: block;
  height: auto;
  /* plafonul v7 era pe container, când el era zona care derula; acum îl poartă
     cardul, iar cadrul se strânge pe el */
  max-height: none;
  margin: 0.125rem;
  overflow: hidden; /* colțurile rotunjite taie conținutul derulat */
  background: var(--card-solid);
  border-radius: var(--r-xl);
  box-shadow: var(--inset-hi), var(--sh-1);
}
/* chenarul în gradient, mutat aici de pe card (aceleași valori) */
.up5-chat .message-container:has(.message-assistant-disclaimer)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  padding: 0.0625rem;
  pointer-events: none;
  opacity: 0.5;
  background: linear-gradient(150deg, rgba(0, 194, 255, 0.5), rgba(124, 92, 252, 0.22) 42%, rgba(181, 33, 233, 0.45) 92%);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

/* Cardul: zona care derulează. Fără fundal și fără ramă proprie — le poartă cadrul. */
.up5-chat .message.message-assistant-disclaimer {
  flex: none;
  width: auto;
  display: block;
  margin: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow-y: auto;
  /* Trio-ul WebKit pentru „cardul nu derulează la touch pe iPhone" (26 aug).
     Videoul cu scrollul mort a arătat că iOS nu trata cardul ca scroller nici
     ÎNAINTE: la pan pe card se mișca DOCUMENTUL (defectul era mascat de cei
     ~70px de scroll mort; odată pagina fixată exact pe viewport, nu se mai
     mișca nimic). Pe Chromium cardul derulează și fără astea — liniile de
     mai jos sunt exclusiv pentru WebKit:
       · touch-action: pan-y — gestul vertical aparține explicit cardului;
       · -webkit-overflow-scrolling — scroller compozitat (fixul istoric
         pentru div-uri overflow care ignoră degetul pe iOS);
       · overscroll-behavior: contain — la capete gestul nu se înlănțuie
         spre document (care oricum nu mai are unde derula). */
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  scrollbar-width: none;
  max-height: min(56svh, 35rem);
  /* estomparea de 22px, acum pe zona care derulează; padding-ul de jos (34px) o
     depășește, deci ultimul rând nu e niciodată voalat */
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 1.375rem), transparent);
  mask-image: linear-gradient(180deg, #000 calc(100% - 1.375rem), transparent);
}
/* rama s-a mutat pe container; pseudo-elementul vechi al cardului ar dubla-o */
.up5-chat .message.message-assistant-disclaimer::before {
  content: none;
}
.up5-chat .message.message-assistant-disclaimer::-webkit-scrollbar {
  display: none;
}
/* CERUT explicit: caseta ocupă 100% din înălțimea disponibilă, până deasupra
   butonului „Sunt de acord"; scroll interior doar când textul depășește.

   O singură regulă, FĂRĂ JS și FĂRĂ măsurători: înălțimea disponibilă e o
   funcție de viewport cu constante de design (header + marginile listei + bara
   de acord + respiro): mobil 71+26+79+32 = 208; desktop 79+26+79+38 = 222.
   `100dvh` = viewportul DINAMIC — browserul îl ține la zi cu bara de adrese,
   deci nu există golul pe care îl lăsa `svh` la sosirea din LP cu bara
   retrasă. `env(safe-area-*)` acoperă crestătura și bara de home.

   ATENȚIE (26 aug): regula de aici e SUPRASCRISĂ de chat-fix10.css §6
   (selector identic, fișier mai târziu: constante 170/184, cardul până lângă
   bara de acord — cerut de Andu pe iPhone real). Ecuația fără scroll mort o
   închide §6b de acolo (padding-bottom-ul stării `chat-pre-disclaimer`).
   Orice ajustare de înălțime a disclaimerului se face ACOLO, nu aici. */
.up5-chat .message.message-assistant-disclaimer {
  height: max(13.75rem, calc(100dvh - 13rem - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)));
  max-height: none;
}
@media (min-width: 720px) {
  .up5-chat .message.message-assistant-disclaimer {
    height: max(13.75rem, calc(100dvh - 13.875rem - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)));
  }
}

/* Containerul casetei e copil de FLEXBOX (lista de mesaje) și lățimea îi venea
   din conținut. În timpul redării „typing" conținutul e gol o clipă → lățime
   aproape zero; cu înălțimea plină, asta se vedea ca o dungă verticală oribilă
   de câțiva px. Lățimea nu are voie să depindă de conținut. */
.up5-chat .message-container:has(.message-assistant-disclaimer) {
  width: 100%;
}

/* HERO-UL DE „CHAT GOL" — reparat dupa acelasi tipar ca regula de mai sus.
   v7 decide starea de chat gol prin PREZENTA in DOM a unui `.message-container`:
     chat-v7.css:1469  [data-empty]:not(:has(.message-container))  → centrarea verticala
     chat-v7.css:1478  :has(.message-container) .acx-welcome        → ascunde hero-ul
   Cardul de disclaimer randat de server E un `.message-container`, iar `:has()` se
   uita la DOM, nu la vizibilitate. Deci si dupa acord, cand cardul e `display: none`,
   v7 credea in continuare ca exista un mesaj: hero-ul disparea si centrarea nu se
   aplica — ecran gol cu sugestiile lipite jos, exact ce s-a raportat.

   Reparatia scoate cardul static din numaratoare, dar NUMAI in starea de dupa acord
   (`html.chat-pre-composer`). Cat timp disclaimerul e pe ecran (`chat-pre-disclaimer`),
   comportamentul v7 ramane neatins: hero ascuns, fara centrare — ca in replica. */
html.chat-pre-composer .up5-chat .js-messages-container[data-empty]:not(:has(.message-container:not([data-disclaimer-static]))) {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: calc(var(--screen-height) - 25rem);
}
html.chat-pre-composer .up5-chat .js-messages-container[data-empty]:not(:has(.message-container:not([data-disclaimer-static]))) .acx-welcome {
  display: block;
}

/* Lista de mesaje nu mai are ce afișa cât timp disclaimerul e pe ecran (e singurul
   mesaj), dar îi scoatem înălțimea ca să nu rămână o zonă derulabilă goală în
   spatele casetei fixe. */
.up5-chat .js-messages-container:has(.message-assistant-disclaimer) { min-height: 0; }


/* =========================================================================
   3. LĂȚIMEA BULELOR pe mobil
   -------------------------------------------------------------------------
   MĂSURAT la 390px: bula lui Andu avea 342px, adică 48px nefolosiți — 12% din
   ecran, pe un conținut care e aproape numai text. Pe mobil marginea de tip
   „conversație" (bula nu atinge marginea, ca să se vadă că e o bulă) costă mai
   mult decât aduce: planul, listele și cardurile de produs sunt deja delimitate
   de propriul lor chenar.

   Bulele UTILIZATORULUI rămân retrase — ele trebuie să se distingă de cele ale
   asistentului, iar mesajele scurte oricum nu ating limita.
   ====================================================================== */
@media (max-width: 719.98px) {
  /* Inset-ul real vine dintr-un singur loc, măsurat: `.grid-container` din chat are
     `padding: 0 14px`. NU adăugăm padding nou (s-ar aduna la el și bula ar ieși mai
     îngustă) — îl reducem la 8px. Câștig net: 12px pe fiecare parte. */
  .up5-chat .grid-container.messages,
  .up5-chat .messages.grid-container { padding-left: 0.5rem; padding-right: 0.5rem; }

  .up5-chat .message-container > .message.message--assistant,
  .up5-chat .message-container > .message--pending {
    max-width: 100%;
    width: auto;
  }
  /* padding interior mai strâns: pe 390px fiecare pixel de text contează */
  .up5-chat .message-container > .message.message--assistant { padding: 0.75rem 0.875rem; }

  /* Bula utilizatorului rămâne vizibil mai îngustă — contrastul de lățime e ceea
     ce face conversația citibilă dintr-o privire. */
  /* CORECTAT: era `max-width: 86%` pe BULĂ. Părintele (`.message-container`) e
     shrink-to-fit — se dimensionează după copil — iar copilul era limitat la 86%
     din părinte: constrângere circulară, pe care browserul o rezolvă strângând la
     min-content. Cu `overflow-wrap` activ, min-content e lățimea unui caracter,
     deci „sigur" ieșea „sig / ur" deși mai era loc berechet pe rând.
     Îngustarea o face oricum containerul, care are `max-width: 86%` raportat la
     COLOANA conversației (chat-v7.css:1197) — acolo procentul are reper fix. */
  .up5-chat .message-container > .message.message--user {
    max-width: 100%;
    width: auto;
  }
}

/* =========================================================================
   4. ȚINTA MORPH-ULUI din landing, decisă ÎNAINTE de primul desen
   -------------------------------------------------------------------------
   Aici era defectul de fond al animației. `chat-v7.css` dă numele de tranziție
   `composer` la două elemente, prin stări pe care le pune MOTORUL:
       .search-bar-container:not([data-hidden]) .input-bar   → cel implicit
       .disclaimer-actions[data-active]                      → când e disclaimer
   Dar motorul rulează în `pse.addToDOMLoad`, adică DUPĂ primul desen. În clipa
   în care browserul face instantaneul paginii noi, bara de input NU are încă
   `data-hidden` (deci ea poartă numele) și bara de disclaimer NU are `data-active`
   (deci nu-l poartă). Rezultat pentru un utilizator nou: pastila din landing se
   morfează în bara de input, care imediat după e ascunsă și înlocuită de bara de
   disclaimer. Morph-ul aterizează pe un element care dispare.

   Tot de aici venea și o clipire pe care nu o ceruse nimeni: bara de input se
   vedea o fracțiune de secundă, apoi era schimbată.

   Reparația: decizia se ia înainte de desen, într-un script inline din twig
   (vezi `twig/chat-v5.diff.md`), care pune pe `<html>` una din două clase.
   CSS-ul de mai jos face restul — determinist, fără să aștepte motorul.
   ====================================================================== */

/* utilizator nou → ținta e butonul de acord; bara de input nici nu apare */
html.chat-pre-disclaimer .up5-chat .search-bar-container .input-bar { view-transition-name: none; }
html.chat-pre-disclaimer .disclaimer-actions { view-transition-name: composer; }
html.chat-pre-disclaimer .up5-chat .search-bar-container { visibility: hidden; }
/* bara de disclaimer e vizibilă din primul cadru: fără ea, instantaneul ar
   surprinde un element cu `opacity: 0` și morph-ul ar ateriza în gol */
html.chat-pre-disclaimer .disclaimer-actions {
  transform: none; opacity: 1; pointer-events: auto;
}

/* utilizator care a acceptat deja → ținta e bara de scriere */
html.chat-pre-composer .disclaimer-actions { view-transition-name: none; }

/* Cardul de disclaimer randat de server (chat.html.twig, `[data-disclaimer-static]`) e în
   HTML din prima cadră, ca textul consimțământului să nu mai aștepte motorul de chat.
   Serverul nu poate ști dacă a fost deja acceptat în sesiunea asta — `sessionStorage` e
   doar al clientului — deci decizia o ia aceeași clasă pusă înainte de primul desen:
   `chat-pre-composer` = a acceptat deja, deci cardul nu are ce căuta pe ecran.
   `display: none` (nu `visibility`) ca să nu lase nici spațiu gol în lista de mesaje. */
/* SPECIFICITATE — aici a fost un bug raportat: disclaimerul reapărea, fără bara de
   acord, lângă input și sugestii, dacă îl acceptaseși fără să scrii nimic.
   Regula de ascundere avea (0,2,1), dar §2 de mai sus pune `display: block` pe
   `.up5-chat .message-container:has(.message-assistant-disclaimer)` = (0,3,0), care
   câștigă. Selectorul lung de mai jos urcă la (0,4,1) și trece înaintea ei.
   Al doilea selector rămâne ca plasă, dacă markup-ul se schimbă. */
html.chat-pre-composer .up5-chat .message-container[data-disclaimer-static],
html.chat-pre-composer [data-disclaimer-static] { display: none; }


/* =========================================================================
   5. FLIP-ul de sosire (rezerva care funcționează în ORICE browser)
   -------------------------------------------------------------------------
   View Transitions între documente merg doar în Chrome/Edge/Safari 18+, și doar
   dacă niciun header nu le taie (Dennis a găsit corect că `Cache-Control:
   no-store`, pus implicit de `session.cache_limiter`, le dezactivează — reparat
   în HomeRouteController și ChatRouteController). În Firefox și pe Safari mai
   vechi nu există deloc.

   `chat-patch2.js` măsoară unde era pastila pe landing și animează ținta din
   acea poziție. Clasele de mai jos sunt doar suportul: JS-ul scrie variabilele.
   ====================================================================== */
.acx-flip-target {
  will-change: transform, opacity;
  animation: acxFlipIn var(--flip-dur, .58s) cubic-bezier(.22, .61, .36, 1) both;
  transform-origin: 50% 50%;
}
/* CONFLICT DE ANIMATII, masurat pe ruta reala: FLIP-ul nu-si juca niciodata
   animatia exact cand conta — la sosirea din landing.

   `.acx-flip-target` are specificitate (0,1,0). Coregrafia de intrare v7 pune
   `animation` pe ACELEASI elemente, cu selectori mult mai puternici:
     · chat-v7.css:3811  body[data-anim-handoff] .disclaimer-actions[data-active]
                         .button-disclaimer-accept        → (0,4,1), acxAttention 1.4s
     · chat-v7.css:3754  body[data-anim-entrance] .up5-chat .search-bar-container
                         :not([data-hidden]) / .disclaimer-actions[data-active]
                                                          → (0,4,1), acxUp .6s
   `animation` e o singura proprietate: cine invinge ia tot. MASURAT inainte de
   reparatie: durata 1.4s si curba cubic-bezier(.4,0,.2,1) pe butonul de acord —
   adica pulsul de atentie, nu morph-ul.

   Nu scoatem coregrafia: o INLANTUIM. Ordinea conteaza — animatia listata ultima
   invinge cat timp e activa. FLIP-ul se termina la .58s, `acxAttention` incepe la
   1.1s si `acxUp` la .16s cu durata .6s, deci suprapunerea reala e minima, iar
   `both` de pe acxFlipIn tine starea finala (`transform: none`) intre ele.
   Selectorii de mai jos adauga `.acx-flip-target`, deci urca la (0,5,1) si trec
   inaintea celor din chat-v7.css. Demo-ul nu prinde conflictul: el reconstruieste
   ecranul de chat si nu ruleaza coregrafia de intrare. */
body[data-anim-handoff] .disclaimer-actions[data-active] .button-disclaimer-accept.acx-flip-target {
  animation:
    acxFlipIn var(--flip-dur, .58s) cubic-bezier(.22, .61, .36, 1) both,
    acxAttention 1.4s var(--ease-inout) 1.1s 2;
}
body[data-anim-entrance] .up5-chat .search-bar-container:not([data-hidden]).acx-flip-target,
body[data-anim-entrance] .disclaimer-actions[data-active].acx-flip-target {
  animation:
    acxFlipIn var(--flip-dur, .58s) cubic-bezier(.22, .61, .36, 1) both,
    acxUp .6s var(--ease-emph) .16s both;
}

@keyframes acxFlipIn {
  from {
    transform: translate(var(--flip-dx, 0px), var(--flip-dy, 0px))
               scale(var(--flip-sx, 1), var(--flip-sy, 1));
    opacity: var(--flip-op, .85);
  }
  to { transform: none; opacity: 1; }
}
/* restul paginii intră în urma ei, ca mișcarea să aibă un singur centru */
body[data-acx-flip] .up5-chat .messages,
body[data-acx-flip] .chatroom-top { animation: acxFlipRest .5s var(--ease-out, ease-out) .08s both; }
@keyframes acxFlipRest { from { opacity: 0; } to { opacity: 1; } }

/* Pe desktop mișcarea e mai scurtă: distanța dintre pastilă și bară e mică, iar
   un morph lung pe un ecran lat devine ostentativ. Andu a cerut explicit
   „pe desktop poate fi ceva mai simplu". */
@media (min-width: 1024px) {
  .acx-flip-target { --flip-dur: .4s; }
  body[data-acx-flip] .up5-chat .messages,
  body[data-acx-flip] .chatroom-top { animation-duration: .34s; }
}

@media (prefers-reduced-motion: reduce) {
  /* Regulile inlantuite de mai sus urca la (0,5,1), iar un `@media` NU adauga
     specificitate — deci fara selectorii repetati aici, `animation: none` ar fi
     pierdut si morph-ul ar rula tocmai pentru cine a cerut mai putina miscare.
     (JS-ul iese oricum din start pe reduced-motion, deci asta e plasa a doua.) */
  .acx-flip-target,
  body[data-anim-handoff] .disclaimer-actions[data-active] .button-disclaimer-accept.acx-flip-target,
  body[data-anim-entrance] .up5-chat .search-bar-container:not([data-hidden]).acx-flip-target,
  body[data-anim-entrance] .disclaimer-actions[data-active].acx-flip-target { animation: none; }
  body[data-acx-flip] .up5-chat .messages,
  body[data-acx-flip] .chatroom-top { animation: none; opacity: 1; }
}

/* 5b. ACORDUL — butonul „Sunt de acord” devine bara de scriere (chat-patch2.js §C)
   -------------------------------------------------------------------------
   `data-acx-acord` stă pe <body> ~0,7s, din clipa în care motorul confirmă acordul.
   · Cele două bare fixe de jos au aceeași suprafață (fundal, blur, linie sus), deci
     își schimbă locul FĂRĂ tranziții: bara de acord dispare, bara de scriere e deja
     la locul ei, iar singura mișcare pe ecran e ținta care zboară din buton. Fără
     asta, bara de scriere urca din `translateY(112%)` sub zbor, iar bara de acord
     cobora pe deasupra lui.
   · Antetul nu se atinge: e deja pe ecran (înainte clipea, prin `data-acx-flip`).
   · Doar lista de mesaje intră lin, ca noul conținut să nu apară „tăiat”. */
body[data-acx-acord] .up5-chat .search-bar-container,
body[data-acx-acord] .disclaimer-actions {
  transition: none;
}
body[data-acx-acord] .up5-chat .js-messages-container {
  animation: acxApareDupaMorph .4s var(--ease-out, ease-out) .06s both;
}
/* cât motorul așteaptă serverul să salveze acordul: butonul arată că a fost apăsat */
.disclaimer-actions .button-disclaimer-accept[data-acx-salvez] {
  opacity: .78;
  cursor: progress;
}
@media (prefers-reduced-motion: reduce) {
  body[data-acx-acord] .up5-chat .js-messages-container { animation: none; }
}


/* =========================================================================
   6. GLOW-UL BAREI DE SCRIERE — un singur strat, un singur timp
   -------------------------------------------------------------------------
   MĂSURAT pe beta14, la 1920×1080: `.input-bar` are DOUĂ pseudo-elemente cu
   același gradient — `::before` la `opacity: .13` (inelul permanent) și `::after`
   la `opacity: 0` (glow-ul de focus). Iar `::before` are `transition: all`.

   De aici „se stinge în frame-uri diferite": două straturi care își schimbă
   opacitatea pe timpi diferiți, plus `all`, care încearcă să interpoleze și
   proprietăți neinterpolabile (`background-image`) — acelea sar, nu tranzitează.

   `transition: all` pe un pseudo-element cu gradient e o capcană dublă: nu poți
   ști ce animă, și animă și ce n-ar trebui. Reparația nu schimbă aspectul, doar
   sincronizează: aceeași proprietate, aceeași durată, același easing, zero delay
   pe ambele straturi. Dacă amândouă se mișcă, se mișcă împreună.
   ====================================================================== */
.up5-chat .search-bar-container .input-bar::before,
.up5-chat .search-bar-container .input-bar::after {
  transition-property: opacity;
  transition-duration: .24s;
  transition-timing-function: cubic-bezier(.22, .61, .36, 1);
  transition-delay: 0s;
}
/* `will-change: opacity` pe glow — SCOS (25 aug): promova glow-ul pe strat
   propriu de compozitor, iar pe iOS, când stratul fix e coborât la închiderea
   tastaturii, stratul promovat rămânea DESENAT la poziția ridicată — „umbra în
   gradient" de deasupra pastilei, văzută pe device după completarea unui
   formular. Tranziția de opacitate merge și fără promovare. */

@media (prefers-reduced-motion: reduce) {
  .up5-chat .search-bar-container .input-bar::before,
  .up5-chat .search-bar-container .input-bar::after { transition: none; }
}


/* =========================================================================
   7. TRANSPARENȚA BAREI ÎN FULLSCREEN — „îmi văd desktopul prin ea"
   -------------------------------------------------------------------------
   MĂSURAT: `.js-search-bar-container` e `position: fixed`, cu
   `background: rgba(8, 11, 18, .82)` și `backdrop-filter: blur(14px) saturate(1.2)`.
   Deci 18% din ea e transparent, iar ce se vede prin ea depinde de ce alege
   compozitorul ca „fundal" de estompat.

   În Safari, un `backdrop-filter` pe un element `fixed` creează o rădăcină de
   backdrop. În fullscreen, compozitorul poate ajunge să eșantioneze fundalul
   FERESTREI în loc de al paginii — și atunci prin cele 18% se vede literal
   desktopul. Nu e o eroare de CSS, e o cale de compunere; nu poate fi „reparată”
   din interior, doar evitată.

   Principiu: un element care ACOPERĂ conținut nu trebuie să depindă de ce e
   dedesubt ca să fie opac. Deci:
     · în ferestruire normală urcăm opacitatea la 94% — încă se citește ca sticlă
       peste gradientul paginii, dar nu mai lasă să treacă nimic real;
     · în fullscreen renunțăm complet la transparență și la blur. Nu se pierde
       nimic: în fullscreen nu există chrome de browser peste care să „plutească”.

   Aceeași corecție pe `.chatroom-top`, care are exact aceeași construcție. Nu a
   fost raportată — headerul e lipit de bara de adrese, deci se observă mai greu —
   dar e același defect și ar apărea în aceleași condiții.
   ====================================================================== */
.up5-chat .search-bar-container,
.up5-chat .chatroom-top {
  background-color: rgba(8, 11, 18, .94);
}

/* `:fullscreen` prinde fullscreen-ul din API (F cu Safari/Chrome pe video sau
   pe document). Pentru fullscreen-ul de FEREASTRĂ al macOS nu există selector,
   deci acoperim și cazul „foarte lat și foarte înalt”, care în practică e el. */
:fullscreen .up5-chat .search-bar-container,
:fullscreen .up5-chat .chatroom-top,
:-webkit-full-screen .up5-chat .search-bar-container,
:-webkit-full-screen .up5-chat .chatroom-top {
  background-color: var(--bg, #080B12);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
@media (min-width: 1600px) and (min-height: 1000px) {
  .up5-chat .search-bar-container,
  .up5-chat .chatroom-top {
    background-color: var(--bg, #080B12);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}


/* =========================================================================
   8. ECRANE LATE — chat-ul nu mai e „o bară pe mijloc"
   -------------------------------------------------------------------------
   MĂSURAT la 1920×1080: sidebar 300px, zona de conținut 1620px, coloana de
   citire 760px, bulele 620px. Adică **62% din zona de conținut e goală**.

   PRIMELE PRINCIPII, ca să nu reparăm în direcția greșită. Limita de citit e
   reală: peste ~75 de caractere pe rând, ochiul pierde începutul rândului
   următor. La 15px/1.6, asta e ~620-700px. **Deci bulele de 620px NU sunt
   defectul** — lățirea lor la 1200px ar strica lizibilitatea, nu ar repara-o.

   Defectul e că spațiul rămas nu are nicio intenție. Trei lucruri, împreună:

     a. Coloana crește ÎN TREPTE, nu proporțional: 760 → 880 (≥1440) → 980
        (≥1800). Bulele urcă la 680 și 720 — capătul de sus al intervalului
        confortabil, nu peste el.
     b. CARDURILE primesc toată coloana. Planul, cardul de produs, formularele și
        cardul de conversie nu sunt proză: sunt tabele, imagini și grile, care
        câștigă din lățime. Ele erau strânse la 620px degeaba.
     c. Coloana capătă un cadru discret, ca să se citească drept „foaie
        deliberată”, nu bandă rămasă. O umbră foarte moale pe verticală, plus
        marginile zonei de conținut ușor mai închise.

   Pragurile: 1024 (desktop), 1440 (desktop mare), 1800 (foarte lat). Jumătatea
   de ecran pe un 1920 cade la ~960px, adică sub 1024 — deci rămâne pe chrome-ul
   mobil, care e deja optimizat. Verificat în ambele screenshot-uri ale lui Andu.
   ====================================================================== */

/* ---- a. coloana în trepte ----
   `!important` pe celulă nu e neglijență: grila framework-ului îi pune lățimea
   cu `!important` (verificat pe beta14 — o regulă cu specificitate 0,5,0 și
   selector pe atribut NU câștigă, doar `!important` o face). Restul regulilor de
   aici sunt fără, pentru că acolo nu e nevoie. */
/* @media (min-width: 1440px) — SCOS pentru 1:1 cu v7.
   Lățea coloana (cell 880/980px) și bulele (assistant 680/720px) pe ecrane mari.
   Replica ține `.cell` la 760px și bula lui Andu la 620px pe ORICE lățime
   (chat-v6.css:44 și :323) — de aici „caseta de chat este mai lată". */
/* @media (min-width: 1800px) — SCOS pentru 1:1 cu v7.
   Lățea coloana (cell 880/980px) și bulele (assistant 680/720px) pe ecrane mari.
   Replica ține `.cell` la 760px și bula lui Andu la 620px pe ORICE lățime
   (chat-v6.css:44 și :323) — de aici „caseta de chat este mai lată". */

/* ---- b. cardurile iau toată coloana: nu sunt proză ---- */
@media (min-width: 1024px) {
  .up5-chat .message-container > .message.message--assistant:has(.chat-meal-plan),
  .up5-chat .message-container > .message.message--assistant:has(.chat-shopping),
  .up5-chat .message-container > .message.message--assistant:has(.event-user-form),
  .up5-chat .message-container > .message--assistant:has(.message__link),
  .up5-chat .message-container > .event-purchase-plan,
  .up5-chat .message-container > .message__link,
  .up5-chat .message-container > .event-user-form,
  .up5-chat .message-container > .event-purchase-plan .plan-switch { max-width: 100%; }
}

/* ---- c. SCOS: „coloana citește ca foaie, nu ca bandă" ----
   Aici stăteau două efecte care se aplicau doar peste 1440px, deci nu se vedeau pe
   ecrane obișnuite — și exact ele făceau zona de mesaje să pară că are alt fundal:

     1. `.up5acc-content` primea două gradiente negre pe margini
        (`rgba(0,0,0,.22)` până la 22% din lățime, stânga și dreapta, cu
        `background-attachment: fixed`). Efectul: toată zona de conținut era
        vizibil mai închisă la margini decât restul paginii.
     2. `.up5-chat .messages .grid-x > .cell` primea
        `box-shadow: 0 0 90px 40px rgba(0,0,0,.18)` — un halou negru de 130px în
        jurul coloanei de mesaje. MĂSURAT pe 1613×830: la 15px sub caseta de
        disclaimer fundalul cobora de la rgb(8,11,18) la rgb(6,9,15) și revenea
        treptat abia după ~50px. Asta era „încadrarea ciudată": un chenar difuz
        în jurul cutiei de mesaje.

   Intenția era să conducă ochiul spre centru pe monitoare late, dar pe o casetă
   care ocupă aproape tot ecranul (disclaimerul) rezultatul e o cutie într-o cutie.
   Fundalul rămâne acum uniform: doar `body` pictează, ca la lățimile mici.
   Limitele de lățime din blocurile a. și b. rămân — acelea sunt layout, nu culoare. */


/* =========================================================================
   9. MENIUL MOBIL NU DERULEAZĂ — cardul de cont rămâne tăiat
   -------------------------------------------------------------------------
   CAUZA, o singură declarație: `.acx-shell` (chat-v7.css:430-433) e
   `position: fixed; inset: 0` cu **`overflow: hidden`**. Pe desktop e corect —
   sidebarul are exact înălțimea ecranului și nimic nu depășește. Pe mobil,
   același element devine meniul pe tot ecranul, iar conținutul lui (logo + 5
   intrări + 2 pastile + cardul „Creează un cont") e mai înalt decât un telefon.
   `overflow: hidden` nu ascunde surplusul politicos: îl taie și îl face
   inaccesibil, fiindcă nu există nicio altă zonă derulabilă.

   Se vede exact ce a raportat Andu: „Am deja cont — autentificare" tăiat la
   jumătate, fără scroll.

   Reparația nu e doar `overflow: auto`. Trei lucruri împreună, altfel apar
   defecte noi:
     · `overscroll-behavior: contain` — fără el, derularea la capătul meniului
       „trece" în pagina de dedesubt (chat-ul se mișcă sub meniul deschis);
     · padding jos cu `safe-area-inset-bottom` — pe iPhone cu bară de gesturi,
       ultimul element ar sta sub ea și tot n-ar fi atingibil;
     · `-webkit-overflow-scrolling: touch` — derulare cu inerție pe iOS vechi.
   ====================================================================== */
@media (max-width: 1023.98px) {
  /* `.acx-nav` e containerul care trebuie să deruleze. E `flex: 1 1 0%` într-o
     coloană flex, deci are nevoie de `min-height: 0` — fără el, un element flex
     refuză să devină mai scurt decât conținutul lui, `scrollHeight` rămâne egal
     cu `clientHeight` și `overflow-y: auto` nu are ce derula. Capcană clasică de
     flexbox, și motivul pentru care prima variantă a acestei reparații (pusă pe
     `.acx-shell`) nu a schimbat nimic: măsurat, `scrollHeight === clientHeight`. */
  .acx-nav {
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;      /* derularea nu „trece" în chat-ul de dedesubt */
    -webkit-overflow-scrolling: touch;
    padding-bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, .22) transparent;
  }
  .acx-nav::-webkit-scrollbar { width: 0.375rem; }
  .acx-nav::-webkit-scrollbar-track { background: transparent; }
  .acx-nav::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .22); border-radius: 6.1875rem; }

  /* Cauza reală a tăierii. `.acx-guest` e flex-item fără `flex-shrink: 0`, deci
     când spațiul se termină flexbox îl STRIVEȘTE, iar `overflow: hidden` taie
     restul. MĂSURAT la 390×660: conținut 181px, comprimat la 66px — de aceea
     „Am deja cont — autentificare" apărea la jumătate.
     Cardul de conversie e exact motivul pentru care meniul există la un musafir;
     el nu se comprimă niciodată. Se comprimă spațiul, sau se derulează. */
  .acx-guest { flex: 0 0 auto; overflow: visible; }
  .acx-card, .acx-pills { flex: 0 0 auto; }

  /* Shell-ul rămâne cu `overflow: hidden` — el e rama ecranului, nu zona de
     conținut. Dacă i-am da și lui scroll, am avea două bare suprapuse. */
  .acx-shell { overflow: hidden; }
  .acx .acx-mbtns { z-index: 98; }     /* butonul de închidere, peste conținutul derulat */
}


/* =========================================================================
   10. CORTINA SPRE ONBOARDING — gradientul care „nu mai aparține"
   -------------------------------------------------------------------------
   CAUZA: `body[data-transitioning-to-onboarding]::before` (chat-v7.css:1146)
   desenează un dreptunghi pe tot ecranul, **complet opac și complet saturat**
   (`#00C2FF → #7C5CFC → #B521E9`), care mătură de jos în sus timp de 0,5s.

   În designul vechi, deschis, gradientul continua paleta paginii. În v7 ambele
   pagini — chat/magazin și onboarding — sunt aproape negre (`#080B12`). Deci
   între două ecrane întunecate apare o jumătate de secundă de cyan-magenta la
   saturație maximă. Nu e o tranziție, e un blitz. Exact „un gradient care nu mai
   e disponibil": culoarea nu mai aparține nicăieri în jurul ei.

   PRIMELE PRINCIPII. O cortină de navigare are un singur rol: să acopere
   schimbarea de pagină fără să atragă atenția asupra ei. Dacă o observi, a
   eșuat. Iar produsul are DEJA un limbaj de tranziție care face asta corect —
   vălul din landing → chat (`body.lp-leaving::before`): bază întunecată, cu o
   urmă de brand doar pe muchia care intră. Două limbaje diferite pentru același
   gest e o inconsecvență în sine.

   Deci nu inventăm nimic: aducem cortina spre onboarding în același limbaj.
   Bază `--bg`, brandul rămâne, dar ca lumină pe muchia de atac, nu ca perete.
   Plus o umbră care o face să pară că vine „peste", nu că apare din senin.
   ====================================================================== */
body[data-transitioning-to-onboarding]::before {
  background:
    /* muchia de atac: brandul, dar ca strălucire, nu ca suprafață */
    radial-gradient(120% 42% at 50% 100%, rgba(124, 92, 252, .38), transparent 62%),
    radial-gradient(90% 30% at 18% 100%, rgba(0, 194, 255, .22), transparent 58%),
    radial-gradient(90% 30% at 82% 100%, rgba(181, 33, 233, .22), transparent 58%),
    var(--bg, #080B12);
  box-shadow: 0 -1.5rem 3.75rem -0.75rem rgba(124, 92, 252, .35);
  animation-duration: .42s;
}
/* Muchia de sus capătă o linie fină de brand — dă direcție mișcării, la costul
   unui singur pseudo-element în plus, și face ca „mătura" să se citească drept
   gest, nu drept dreptunghi. */
body[data-transitioning-to-onboarding]::after {
  content: ""; position: fixed; left: 0; right: 0; top: 0; height: 0.125rem;
  z-index: 1000000; pointer-events: none;
  background: linear-gradient(90deg, #00C2FF, #7C5CFC 50%, #B521E9);
  transform-origin: 50% 0;
  animation: acxCurtainEdge .42s var(--ease-emph) both;
}
@keyframes acxCurtainEdge {
  from { transform: translateY(-100vh) scaleX(.2); opacity: 0; }
  60%  { opacity: 1; }
  to   { transform: none; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  body[data-transitioning-to-onboarding]::before { animation: none; }
  body[data-transitioning-to-onboarding]::after { display: none; }
}


/* =========================================================================
   11. CERCUL LUI ANDU ȘI BARA DE SCRIERE, puțin mai mari
   -------------------------------------------------------------------------
   Cerere directă: cercul cu 10-20% mai mare, bara „o idee" mai mare. Am ales
   +18% pe cerc (44 → 52px) și +2px pe înălțimea barei, cu textul la 15,5px.

   De ce +18% și nu +10%: la 44px, avatarul era mai mic decât butonul de meniu
   din colț (44px) — adică Andu cântărea vizual cât un buton de sistem. La 52px
   devine cel mai mare element din header, ceea ce e corect: el e subiectul.
   Peste ~56px ar începe să împingă titlul, care are nevoie de două rânduri.

   Bara: +2px înălțime și text de la 15 la 15,5px. Nu mai mult — bara e `fixed`
   și fiecare pixel al ei e luat din zona de conversație, care e mai valoroasă.
   ====================================================================== */
/* SCOS pentru 1:1 cu v7 (cerut explicit: „poza cu Andu este mai mare").
   Regula de aici avea și un defect de construcție, independent de mărime: dădea
   dimensiunea AMBELOR — wrapperului ȘI imaginii — deși wrapperul are `padding: 2px`.
   La 52px pe wrapper, cutia de conținut rămâne 48px, deci imaginea forțată la 52px
   ieșea din ea. MĂSURAT: wrapper 52×52, dar `<img>` 48×52 — fotografie întinsă pe
   verticală într-un decupaj rotund de 48px, iar inelul (`::before`, `inset: -3px`)
   fiind centrat pe wrapper, aura părea deplasată față de poză. Exact ce s-a raportat.

   v7 dimensionează DOAR imaginea (40×40) și lasă wrapperul să rezulte din
   `inline-flex` + padding → 44×44, perfect concentric (chat-v6.css:258-265).
   Dacă mărimea se dorește iar mai mare, se schimbă NUMAI `img`, niciodată wrapperul.

.up5-chat .chatroom-top-image,
.up5-chat .chatroom-top-image img {
  width: 52px; height: 52px;
}
.up5-chat .chatroom-top-image::before { inset: -3px; }
*/

/* Punctul verde „online" de pe avatar — 1:1 cu v7.
   CAUZA, sistemica: framework.bundle.css declara `*, ::before, ::after
   { box-sizing: border-box }`, adica INCLUDE pseudo-elementele. base-v6.css din
   replica il pune doar pe `*`, iar `*` NU prinde pseudo-elemente. Punctul are
   `width/height: 11px` + `border: 2.5px`, deci:
     · in replica (content-box): 11 + 2×2,5 = 16px vizual, la 28-29px de colt;
     · la noi (border-box):      11px vizual, la 33px de colt — mai mic si mai infundat.
   MASURAT: `inset` 29px vs 33px. Il readucem la constructia din replica.
   ATENTIE: aceeasi diferenta poate atinge orice pseudo-element din chat-v7.css
   dimensionat cu width/height + border. Asta e singurul gasit pana acum. */
.up5-chat .chatroom-top-image::after { box-sizing: content-box; }

/* SCOS pentru 1:1 cu v7 (raportat: „inputul e nejustificat de mare").
   MASURAT pe 1440x900: bara ajungea la 88px in loc de 60px — cu 28px mai inalta —
   fiindca `padding: 20px` uniform inlocuia padding-ul asimetric al v7
   (`6px 6px 6px 18px`, chat-v6.css:1047): +14px sus si +14px jos. Containerul
   crestea de la 85 la 113px, iar fontul scadea de la 16 la 15,5px.
   v7 are o singura definitie, fara suprascriere pe mobil, deci scotand-o revenim
   la valorile lui pe toate latimile.

.up5-chat .search-bar-container .input-bar { padding: 20px; }
.up5-chat .search-bar-container textarea,
.up5-chat .search-bar-container .input-bar textarea { font-size: 15.5px; }
*/

@media (max-width: 719.98px) {
  /* avatarul pe mobil: scos la fel, vezi nota de mai sus */
  /* padding-ul barei pe mobil: scos la fel, v7 nu are suprascriere aici */
}


/* =========================================================================
   12. URMA FORMULARULUI COMPLETAT (vezi `chat-patch2.js` F)
   -------------------------------------------------------------------------
   Chitanța trebuie să arate a ISTORIC, nu a card activ: fără inel gradient,
   fără umbră, fundal mai stins decât al mesajelor. Dacă ar arăta ca un card
   viu, utilizatorul ar încerca să o editeze.
   ====================================================================== */
.up5-chat .acx-form-receipt {
  margin-top: 0.625rem;
  padding: 0.75rem 0.875rem;
  border: 0.0625rem dashed var(--card-bd);
  border-radius: var(--r-lg, 0.875rem);
  background: rgba(255, 255, 255, .022);
  animation: acxRise .4s var(--ease-emph) both;
}
.up5-chat .acx-form-receipt-h {
  display: flex; align-items: center; gap: 0.4375rem;
  font-size: 0.78125rem; font-weight: 700; color: var(--fg-2, #B9BDC9);
}
.up5-chat .acx-form-receipt-h::before {
  content: "✓";
  display: inline-flex; align-items: center; justify-content: center;
  width: 1rem; height: 1rem; border-radius: 50%;
  background: rgba(52, 199, 132, .18); color: #4ADE9B;
  font-size: 0.625rem; font-weight: 800; flex: 0 0 auto;
}
.up5-chat .acx-form-receipt-l {
  display: flex; flex-wrap: wrap; gap: 0.375rem 0.5rem; margin-top: 0.5625rem;
}
.up5-chat .acx-form-receipt-i {
  font-size: 0.75rem; color: var(--muted);
  padding: 0.1875rem 0.5625rem; border-radius: 6.1875rem;
  background: rgba(255, 255, 255, .04);
  border: 0.0625rem solid var(--hairline, rgba(255, 255, 255, .06));
}
.up5-chat .acx-form-receipt-i b { color: var(--fg-2, #B9BDC9); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .up5-chat .acx-form-receipt { animation: none; }
}

/* =========================================================================
   13. MODALUL „ÎȚI TREBUIE CONT" (pisica) — încape pe un ecran de telefon
   -------------------------------------------------------------------------
   MĂSURAT la 390×844: containerul avea 767px, adică tot ecranul minus 77.
   Pe telefoane reale, mai scunde (375×667, 360×640), depășea și devenea
   derulabil — un modal de conversie nu are voie să-și ascundă butoanele.

   Ce consuma înălțimea, în ordine: imaginea 180×240 (cea mai mare bucată
   dintre toate), padding-ul de 34/28px al cardului, titlul de 24px.
   Reducerile de mai jos sunt DOAR pe mobil; pe desktop rămâne cum era.
   ====================================================================== */
@media (max-width: 719.98px) {
  /* imaginea: 180 → 120px lățime, deci ~240 → ~160px înălțime (raport 3:4).
     Cel mai mare câștig dintr-o singură valoare, fără să atingem conținutul. */
  #modal.modal-pop-up--create-account .modal__content img.asteptam,
  .popup-custom img.asteptam {
    max-width: 7.5rem;
  }
  /* cardul: padding mai strâns, dar nu lipit de text */
  #modal .modal__wrapper {
    padding: 1.375rem 1.125rem 1.25rem;
  }
  /* titlul: 24 → 21px; rămâne cel mai mare element de text */
  #modal .modal__content .font-bold.small-font-24 {
    font-size: 1.3125rem;
  }
  /* ritmul vertical dintre blocuri, o idee mai strâns */
  #modal .modal__content .small-margin-children-10 > * + * {
    margin-top: 0.4375rem;
  }
}

/* Ecrane SCUNDE (iPhone SE 375×667, Android 360×640): dupa reducerile de mai sus mai
   rămâneau 17-20px de derulare. Pragul e pe ÎNĂLȚIME, nu pe lățime — problema e
   verticală, iar pe telefoanele înalte imaginea nu are motiv să scadă mai mult. */
@media (max-width: 719.98px) and (max-height: 700px) {
  #modal.modal-pop-up--create-account .modal__content img.asteptam,
  .popup-custom img.asteptam {
    max-width: 6rem;
  }
  #modal .modal__wrapper {
    padding: 1.125rem 1rem 1rem;
  }
}

/* Sub 660px de înălțime (Android 360×640) mai rămâneau 12px. Nu le luăm din blocul de
   beneficii — acela e deja la 13,5px cu line-height 1,55, iar el poartă argumentele de
   conversie. Le luăm iar din imagine, singurul element decorativ. */
@media (max-width: 719.98px) and (max-height: 660px) {
  #modal.modal-pop-up--create-account .modal__content img.asteptam,
  .popup-custom img.asteptam {
    max-width: 5.25rem;
  }
}

/* ---------------------------------------------------------------------------
   ȚINTA MORPH-ULUI, ASCUNSĂ PÂNĂ PORNEȘTE FLIP-UL
   `chat-patch2.js` (§B) rulează `defer`, deci abia DUPĂ ce browserul a desenat
   pagina o dată. Ținta apărea așezată la locul ei, apoi sărea la punctul de
   plecare al animației și abia apoi se mișca — „așezat, animat, așezat".
   Clasa e pusă înainte de primul desen de scriptul inline din `chat-v5.html.twig`,
   doar când chiar există un dreptunghi de predare din landing, și e scoasă de
   `elibereazaTinta()` pe ORICE drum (animație pornită, sărită, sau plasa de 2,5s).
   `visibility`, nu `display`: geometria rămâne calculabilă, iar FLIP-ul are nevoie
   de ea ca să măsoare unde trebuie să ajungă.
   ------------------------------------------------------------------------ */
html.acx-flip-pending .up5-chat .search-bar-container .input-bar,
html.acx-flip-pending .disclaimer-actions .button-disclaimer-accept {
  visibility: hidden;
}

/* ---------------------------------------------------------------------------
   POARTA COREGRAFIEI — îngheț la cadra ZERO până când pagina e gata.
   `data-anim-hold` e pus înainte de primul desen (scriptul inline din
   `chat-v5.html.twig`) și scos o singură dată, când `load` + `document.fonts.ready`
   s-au încheiat (sau la plafonul de 1500ms).

   DE CE MERGE: toate coregrafiile din §12 au `animation-fill-mode: both`. O animație
   în pauză la timpul 0, cu `both`, randează exact PRIMA cadră — deci elementele se
   încarcă în starea lor de început de animație (translate + opacity 0), nu în cea
   finală. Ceasul nu avansează cât e pauză, deci la eliberare toate pleacă de la 0
   real și în același timp. Fără asta, ceasul pornea la primul calcul de stil și mergea
   în timp ce fonturile și imaginile încă soseau — de unde „animația continuă dintr-o
   poziție aleatoare".

   Selectorii îi oglindesc pe cei din `chat-v7.css` §12; `animation-play-state` nu se
   moștenește, deci trebuie numite elementele animate, nu un strămoș.
   ------------------------------------------------------------------------ */
body[data-anim-hold] .chatroom-top,
body[data-anim-hold] .up5-chat .search-bar-container:not([data-hidden]),
body[data-anim-hold] .disclaimer-actions[data-active],
body[data-anim-hold] .acx-welcome,
body[data-anim-hold] .acx-shell,
body[data-anim-hold] .acx-card,
body[data-anim-hold] .acx-pills,
body[data-anim-hold] .acx-guest,
body[data-anim-hold] .up5-chat .input-bar::before,
body[data-anim-hold] .disclaimer-actions[data-active] .button-disclaimer-accept,
/* și cele două adăugate mai jos, pentru sosirea din landing — altfel ar porni
   singure la primul desen, cât restul stă în pauză, adică exact desincronizarea
   pe care poarta o elimină */
body[data-anim-hold] .up5-chat .js-messages-container,
body[data-anim-hold] .acx-mbtns,
/* suprafața barei de jos, mutată pe `::before` (vezi blocul de la finalul fișierului) */
body[data-anim-hold] .up5-chat .chat-suggestions,
body[data-anim-hold] .disclaimer-actions::before,
body[data-anim-hold] .up5-chat .search-bar-container:not([data-hidden])::before,
body[data-anim-hold] .disclaimer-actions .button-disclaimer-decline {
  animation-play-state: paused !important;
}
/* Sub „mișcare redusă" poarta nici nu se armează (vezi `chat-v5.html.twig`), deci
   `data-anim-hold` nu ajunge niciodată pe pagină acolo — nu e nevoie de contra-regulă. */

/* ---------------------------------------------------------------------------
   SOSIREA DIN LANDING: ÎNTÂI DOAR ELEMENTUL CARE SE MORFEAZĂ, APOI PAGINA.
   -----------------------------------------------------------------------------
   CE ERA. Coregrafia de intrare pornea odată cu morph-ul: bara de sus la 0,05s,
   meniul la 0s, hero-ul la 0,3s — iar cardul de disclaimer, fiind randat de server,
   nu era animat deloc, deci se vedea din prima cadră. Rezultatul: pagina apărea
   întreagă, și abia peste ea răsărea butonul zburând. Citit ca „s-a încărcat pagina,
   apoi a apărut ceva din senin", nu ca o continuare a landing-ului.

   CE FACEM. Pe sosirea din landing (`data-anim-handoff`) singurul element care se
   mișcă la început e ținta morph-ului — pastila de căutare devine butonul de acord
   sau bara de scriere. Tot restul intră DUPĂ ce ea aterizează (FLIP = 0,58s).

   Nu rescriem coregrafia, doar îi mutăm plecarea: `animation-delay` e proprietate
   separată, deci schimbarea ei păstrează animația, curba și durata din chat-v7.css.
   Ordinea dintre elemente se păstrează, doar decalată în bloc.

   TOATE plecările sunt DUPĂ 0,58s — durata FLIP-ului (`--flip-dur`). Prima versiune
   le punea la 0,5s, adică peste ultimii 80ms ai morph-ului: se vedea cum restul
   paginii începe să apară în timp ce butonul încă zboară. Acum ținta chiar aterizează
   întâi, și abia apoi începe restul.
   ------------------------------------------------------------------------ */
body[data-anim-handoff] .chatroom-top { animation-delay: .6s; }
body[data-anim-handoff] .acx-shell    { animation-delay: .6s; }
body[data-anim-handoff] .acx-card     { animation-delay: .72s; }
body[data-anim-handoff] .acx-pills    { animation-delay: .8s; }
body[data-anim-handoff] .acx-guest    { animation-delay: .88s; }
body[data-anim-handoff] .acx-welcome  { animation-delay: .9s; }

/* Suprafața conversației (inclusiv cardul de disclaimer randat de server) nu avea
   nicio animație de intrare — de aceea se vedea din prima cadră. Îi dăm una, doar
   pe opacitate: `.js-messages-container` e zona care derulează, iar un `transform`
   pe ea ar muta și poziția de scroll în timpul animației. */
body[data-anim-handoff] .up5-chat .js-messages-container {
  animation: acxApareDupaMorph .45s var(--ease-out) .88s both;
}
/* Burgerul mobil stă în afara `.acx-shell`, deci scăpase de coregrafie și rămânea
   vizibil singur în colț cât timp restul era ascuns. */
body[data-anim-handoff] .acx-mbtns {
  animation: acxApareDupaMorph .4s var(--ease-out) .6s both;
}
@keyframes acxApareDupaMorph {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ---------------------------------------------------------------------------
   BARA CARE ȚINE BUTONUL — apare ODATĂ CU mișcarea lui, nu înaintea ei.
   -----------------------------------------------------------------------------
   DE CE scăpase de poartă: bara nu intră printr-o ANIMAȚIE, ci printr-o TRANZIȚIE
   declanșată de motor când îi pune `data-active` (chat-v7.css:3203 / 2811).
   `animation-play-state: paused` nu atinge tranzițiile, deci bara aluneca în sus și
   era complet vizibilă cu mult înainte ca poarta să se deschidă. Pe ecran se vedea
   bara plină, cu butonul zburând prin ea — exact invers față de ce trebuie.

   DE CE nu putem pur și simplu să stingem bara: butonul care se morfează e COPILUL
   ei. `opacity: 0` pe bară l-ar stinge și pe el, adică tocmai singurul lucru care
   trebuie văzut.

   SOLUȚIA: îi mutăm suprafața (fundal, blur, linia de sus) pe un `::before` și
   stingem doar pseudo-elementul. Butonul, fiind copil normal, rămâne neatins.
   `z-index: -1` îl ține sub conținut; bara are `z-index` propriu, deci creează
   context de stivuire și pseudo-elementul nu poate coborî sub ea.

   Pornește la 0,12s — după ce butonul s-a urnit, cum trebuie — și se termină la
   0,52s, cu puțin înainte de aterizare (0,58s), ca bara să fie deja acolo când
   butonul se așază în ea.
   ------------------------------------------------------------------------ */
/* FĂRĂ `[data-active]`, deliberat. Atributul e pus de MOTOR, deci abia după primul
   desen — dar bara e forțată vizibilă din prima cadră mai sus în fișier
   (`html.chat-pre-disclaimer .disclaimer-actions { opacity: 1 }`, ca instantaneul
   morph-ului să nu prindă un element transparent). Legând regulile de `[data-active]`,
   ele începeau să se aplice abia când sosea motorul — deci bara apărea întreagă, apoi
   se stingea, apoi revenea odată cu butonul. Exact clipirea raportată. */
body[data-anim-handoff] .disclaimer-actions,
body[data-anim-handoff] .up5-chat .search-bar-container:not([data-hidden]) {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-top-color: transparent;
}
body[data-anim-handoff] .disclaimer-actions::before,
body[data-anim-handoff] .up5-chat .search-bar-container:not([data-hidden])::before {
  content: "";
  position: absolute;
  inset: -0.0625rem 0 0;
  z-index: -1;
  pointer-events: none;
  background: rgba(8, 11, 18, 0.82);
  -webkit-backdrop-filter: blur(0.875rem) saturate(1.2);
  backdrop-filter: blur(0.875rem) saturate(1.2);
  border-top: 0.0625rem solid var(--hairline);
  animation: acxApareDupaMorph .32s var(--ease-out) .26s both;
}
/* Butonul X („refuz") e COPIL al barei, deci suprafața mutată pe `::before` nu-l
   acoperea — rămânea vizibil din prima cadră și trăda conturul barei chiar dacă
   fundalul ei era stins. Intră odată cu suprafața.
   Perechea din bara de scriere nu are nevoie de asta: acolo ținta morph-ului
   (`.input-bar`) e îngropată în grila barei, deci n-are frați vizibili la același
   nivel pe care să-i putem stinge fără s-o stingem și pe ea. */
body[data-anim-handoff] .disclaimer-actions .button-disclaimer-decline {
  animation: acxApareDupaMorph .32s var(--ease-out) .26s both;
}
/* ═══════════════════════════════════════════════════════════════════════════
   PATCH 3 — se adaugă la SFÂRȘITUL lui public/assets/css/routes/chat-patch2.css
   (obligatoriu DUPĂ blocul „POARTA COREGRAFIEI", altfel pierde la ordine)
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---------------------------------------------------------------------------
   EXCEPȚIE LA POARTĂ — obiectul urmărit de ochi nu are voie să fie înghețat.

   `data-anim-hold` pune `animation-play-state: paused !important` pe lista de
   elemente din coregrafie, iar butonul de acord și bara de scriere sunt în acea
   listă. Cât timp poarta e închisă, morph-ul pornit inline (vezi patch-ul din
   `chat-v5.html.twig`) era pus în pauză odată cu restul — adică exact ce nu
   trebuie: el e singurul lucru care are voie să se miște în intervalul ăla.

   Regula asta îl lasă să meargă. Restul paginii rămâne înghețat până la poartă,
   ceea ce e chiar intenția scrisă la blocul „SOSIREA DIN LANDING" de mai sus:
   „întâi doar elementul care se morfează, apoi pagina".

   DE CE TREI SELECTORI. Blocul de pauză urcă până la specificitate (0,4,1)
   (`body[data-anim-hold] .disclaimer-actions[data-active] .button-disclaimer-accept`).
   Cu `!important` de ambele părți, la specificități diferite câștigă tot cea mai
   specifică — deci un singur selector scurt ar putea pierde în clipa în care
   motorul pune `data-active`. Primii doi urcă la (0,5,1) și acoperă exact cele
   două ținte posibile ale morph-ului; al treilea rămâne pentru orice altceva.
   E același tipar folosit la blocul de specificitate de mai sus în fișier.
   ------------------------------------------------------------------------ */
body[data-anim-hold] .disclaimer-actions[data-active] .button-disclaimer-accept.acx-flip-target,
body[data-anim-hold] .up5-chat .search-bar-container:not([data-hidden]) .input-bar.acx-flip-target,
body[data-anim-hold] .acx-flip-target {
  animation-play-state: running !important;
}

/* ---------------------------------------------------------------------------
   1. SUGESTIILE DE CHAT — intră cu pagina, nu în timpul morph-ului.
   -----------------------------------------------------------------------------
   Pe sosirea din landing a unui utilizator care a acceptat deja disclaimerul, ținta
   morph-ului e bara de scriere. MĂSURAT pe ruta reală: la 221ms de la pornirea
   morph-ului, sugestiile erau deja pe ecran, iar bara zbura peste ele.

   DE CE scăpaseră: ele nu apar printr-o ANIMAȚIE, ci printr-o TRANZIȚIE pe
   `opacity`/`transform`, declanșată când motorul pune `data-active`
   (chat-v7.css:3007-3013). Poarta oprește `animation-play-state`, care nu atinge
   tranzițiile — aceeași scăpare ca la suprafața barei de jos.
   Le dăm o animație proprie, pornită după aterizare, ca restul paginii.
   ------------------------------------------------------------------------ */
body[data-anim-handoff] .up5-chat .chat-suggestions {
  animation: acxApareDupaMorph .4s var(--ease-out) .88s both;
}

/* ---------------------------------------------------------------------------
   2. GLOW-UL BUTONULUI DE ACORD — încadrat, ca să nu mai fie retezat.
   -----------------------------------------------------------------------------
   `.cta--background-gradient` are `box-shadow: 0 12px 28px -6px` — un halou împins
   12px în JOS. MĂSURAT: butonul se termină la y=679, glow-ul ajunge la y=713, iar
   ecranul are 693 — deci **20px din el cad sub marginea de jos**. Sub buton există
   doar 14px de bară, fiindcă bara e `position: fixed; bottom: 0`.
   Efectul e exact cel raportat: haloul pare urcat (i se vede doar jumătatea de sus)
   și se termină într-o linie dreaptă, ca o cutie.

   Reparația îl centrează și îl strânge, ca să încapă în cei 14px. Umbra închisă
   (`0 2px 8px`) rămâne — ea oricum se încadra. Regula e scopată DOAR pe butonul
   ăsta: `.cta--background-gradient` e folosit și pe landing și în magazin, unde are
   loc sub el și arată corect.
   ------------------------------------------------------------------------ */
.disclaimer-actions .button-disclaimer-accept.cta--background-gradient,
.disclaimer-actions .button-disclaimer-accept.cta--background-gradient:hover {
  box-shadow:
    0 0.125rem 0.5rem rgba(0, 0, 0, 0.3),
    0 0 1.25rem -0.25rem rgba(124, 92, 252, 0.55);
}

/* Textul butonului de acord, tăiat jos pe iOS. `.font-gradient` pune
   `-webkit-background-clip: text`, iar regula care îl anulează pe buton
   (chat-v7.css:3247) schimbă doar culoarea și `text-fill-color`, nu și decuparea.
   Cu `background: none` + decupare pe text, WebKit poate reteza coborâtoarele.
   Anulăm și decuparea. */
.button-disclaimer-accept .font-gradient {
  -webkit-background-clip: border-box;
  background-clip: border-box;
}

/* ---------------------------------------------------------------------------
   SPAȚIEREA CONVERSAȚIEI — mai strânsă, cerut.
   Valorile v7 (gap 18px între bule, `line-height: 1.6` în ele) sunt identice cu
   replica, deci nu erau o abatere — dar pe ecran citesc aerisit. Strângem ambele
   pârghii, fiindcă distanța percepută între „liniile de chat" vine din amândouă:
   spațiul dintre bule ȘI înălțimea rândului dinăuntru.
   Bula de disclaimer NU se atinge: are propriul ritm de lectură, iar textul e lung.
   ------------------------------------------------------------------------ */
.up5-chat .js-messages-container {
  /* 25 aug: „50% din cât e acum" — jumătate din 13px (densificarea veche
     peste 18px-ul din chat-v7.css), cerut de Dennis */
  gap: 0.40625rem;
}
.up5-chat .message:not(.message-assistant-disclaimer) {
  line-height: 1.5;
}
.up5-chat .message:not(.message-assistant-disclaimer) p + p,
.up5-chat .message:not(.message-assistant-disclaimer) p + ul,
.up5-chat .message:not(.message-assistant-disclaimer) ul + p {
  margin-top: 0.4375rem;
}

/* ---------------------------------------------------------------------------
   BULELE SCURTE SE RUPEAU LA MIJLOCUL CUVÂNTULUI („sal / ut", raportat).
   `chat-v7.css:1234` pune `word-break: break-word` pe bulă. Proprietatea NU e
   sinonimă cu `overflow-wrap: break-word`: prin definiție se comportă ca
   `word-break: normal` + `overflow-wrap: anywhere`, iar `anywhere` **intră în
   calculul dimensiunii intrinseci** — min-content devine lățimea celui mai lat
   CARACTER. Bula e dimensionată shrink-to-fit (containerul are `align-self`,
   deci nu se întinde), așa că motorul are voie s-o strângă până la un caracter,
   deși pe rând mai era loc berechet.
   `overflow-wrap: break-word` rupe la fel cuvintele care chiar nu încap (URL-uri
   lungi), dar NU afectează dimensiunea intrinsecă — deci bula se întinde cât
   textul, iar ruperea rămâne doar ca plasă.
   ------------------------------------------------------------------------ */
.up5-chat .message {
  word-break: normal;
  overflow-wrap: break-word;
}

/* ---------------------------------------------------------------------------
   CAPUL CONVERSAȚIEI — trei reglaje cerute.
   MĂSURAT înainte: bară 57px, avatar 40px, nume 15,5px, status 12,5px, buton de
   meniu 44px cu centrul la 31px față de centrul barei la 28 (3px mai jos) și la
   12px de marginea dreaptă, față de 14px cât are avatarul în stânga.
   ------------------------------------------------------------------------ */

/* 1. Bara „Andu AI / scrie", o idee mai mare — DOAR prin conținut, nu prin padding.
      Prima încercare mărea și `padding` pe `.grid-x`: bara sărea de la 57 la 85px,
      adică +50%, mult peste „o idee". Cu avatarul și textul mărite, bara crește
      singură cât trebuie. */
/* Avatarul se mărește prin IMAGINE, nu prin cutie. Inelul colorat E chiar
   `padding: 2px` peste `background: var(--grad)` de pe container, iar imaginea are
   la rândul ei `border: 2px solid var(--bg)`. Fixând lățimea containerului la 44px
   forțam un conținut de 48px într-o cutie de 44 — inelul se strivea inegal, exact
   „border-ul nu e uniform/centrat". Lăsăm containerul să se dimensioneze singur. */
.up5-chat .chatroom-top-image img {
  width: 2.75rem;
  height: 2.75rem;
}
@media (max-width: 719.98px) {
  .up5-chat .chatroom-top-image img {
    width: 2.5rem;
    height: 2.5rem;
  }
}
.up5-chat .chatroom-top .font-bold.large-font-18 {
  font-size: 1.03125rem;
}
.up5-chat .chatroom-top [data-js="chatroom-top-message"] {
  font-size: 0.8125rem;
  height: 1rem !important;
}

/* 2. Butonul de meniu, încadrat: centrat pe bară și la aceeași distanță de
      margine ca avatarul în partea cealaltă. Bara devine 65px cu punctul 1,
      deci (65 − 44) / 2 ≈ 10px. */
/* MUTAT în chat-v7.css (7 sept, Dennis: navigație identică pe toate paginile
   de dashboard): `.acx-mbtns { top: 13px / 17px; right: 14px }` se aplica doar
   pe chat, iar pe magazin/cont toggle-ul rămânea la 9px - acum e în CSS-ul comun. */

/* 3. Aura de scriere se stinge lin, nu dintr-o dată.
      Era `transition: opacity .6s` — dar inelul se rotește continuu, așa că la
      stingere se vedea mai degrabă o tăietură decât o așezare. Adăugăm și o
      relaxare de scară: cercul „se retrage", nu dispare. */
.up5-chat .chatroom-top-image::before {
  transition:
    opacity 0.9s var(--ease-out),
    transform 0.9s var(--ease-out);
  transform: scale(0.94);
}
.up5-chat .chatroom-top-image[data-active]::before {
  transition:
    opacity 0.3s var(--ease-out),
    transform 0.45s var(--ease-emph);
  transform: scale(1);
}

/* ---------------------------------------------------------------------------
   MENIUL DIN CHAT, DESCHIS PE MOBIL — conținutul urcă, logo-ul la fel ca pe LP.
   MĂSURAT: `.acx-shell` are `padding-top: calc(64px + safe-area)`, rezervat
   pentru burgerul fixat sus. Dar burgerul stă sus-DREAPTA (8→52px), iar logo-ul
   sus-STÂNGA — nu se ating pe orizontală, deci cei 64px erau spațiu gol. Logo-ul
   începea la 77px, primul element de meniu la 141px.
   Cu 14px, capul meniului se aliniază pe aceeași linie cu burgerul, ca pe LP.
   Logo-ul: 124px aici, 120px pe LP după micșorare — le facem identice.
   ------------------------------------------------------------------------ */
/* MUTAT în chat-v7.css (7 sept, Dennis: navigație identică pe toate paginile
   de dashboard) - blocul `.acx-shell { padding-top: 8px }` + `.acx-brand svg
   { width: 120px }` de aici se aplica doar pe chat; acum e în CSS-ul comun. */

/* ---------------------------------------------------------------------------
   X-ul ȘI LOGO-UL, PE ACEEAȘI LINIE CÂND MENIUL E DESCHIS.
   Butonul e `position: fixed`, deci are o singură poziție — dar are DOUĂ contexte:
     · meniu închis → plutește peste capul conversației (61px), centru la 30px;
     · meniu deschis → capul conversației e acoperit, iar reperul devine logo-ul
       din meniu, al cărui centru e la 42px (14px padding pe shell + 13px până la
       logo + 29/2).
   MĂSURAT: 12px diferență, exact cât se vede în captură. Îl coborâm doar în
   starea deschisă: 42 − 44/2 = 20px.
   ------------------------------------------------------------------------ */
/* CORECTAT: nu X-ul se mută, ci logo-ul urcă la el (vezi padding-ul de pe
   `.acx-shell` de mai sus). Un buton care sare la deschiderea meniului se citește
   ca o eroare, chiar dacă ajunge într-un loc corect. */

/* ---------------------------------------------------------------------------
   Ultimul reglaj de aliniere, pus la FINAL intentionat: blocul `max-width: 1023.98px`
   de mai sus fixeaza `padding-top: 3px` pe shell, iar o regula cu acelasi rang pusa
   inainte pierde la ordine. Aici castiga.
   Intre 720 si 1024px bara e 69px (avatar 48), deci X-ul sta cu 4px mai jos; logo-ul
   il urmeaza cu aceiasi 4px, ca centrele sa coincida ca pe telefon.
   ------------------------------------------------------------------------ */
/* (și reglajul 720-1024px de 12px e mutat în chat-v7.css, vezi mai sus) */

/* ---------------------------------------------------------------------------
   AURA AVATARULUI din capul conversației era lipită de marginea de sus.
   MĂSURAT: avatarul 8→52 într-o bară de 61px; aura are `inset: -3px` plus
   `blur(4px)`, deci ajunge la y=1 pe o bară care începe la y=0 — un singur pixel
   de joc. Vizual se citește ca tăiată, și ca descentrată, fiindcă sub avatar
   rămâne mai mult loc decât deasupra.
   Adăugăm 5px de respiro simetric pe rândul din bară: aura capătă ~6px deasupra,
   iar avatarul redevine centrat pe verticală. Bara crește de la 61 la 71px.
   ------------------------------------------------------------------------ */
.up5-chat .chatroom-top .grid-x {
  padding: 0.3125rem 0;
}

/* ---------------------------------------------------------------------------
   HERO-UL DE CHAT GOL, CENTRAT CU ADEVĂRAT.
   Regula v7 centrează cu `justify-content: center` într-o cutie de
   `calc(--screen-height - 400px)`. MĂSURAT pe 844px: cutia iese 444px, dar zona
   liberă dintre capul conversației (jos la 71) și sugestii (sus la 587) e de
   516px — deci hero-ul se centra într-o cutie mai mică decât spațiul real și
   ieșea cu 28px mai sus decât mijlocul.
   Rezerva corectă e suma a ce ocupă marginile: bara 71 + sugestiile 169 + bara de
   scriere 85 = 325px, plus 18px de ajustare fina masurata pe ecran (343). Se aplică pe amândouă regulile de centrare — cea din v7 și
   cea pentru starea de după acceptarea disclaimerului.
   ------------------------------------------------------------------------ */
.up5-chat .js-messages-container[data-empty]:not(:has(.message-container)),
html.chat-pre-composer .up5-chat .js-messages-container[data-empty]:not(:has(.message-container:not([data-disclaimer-static]))) {
  min-height: calc(var(--screen-height) - 21.4375rem);
}

/* ---------------------------------------------------------------------------
   BARA DE SCRIS, PREA SUS DEASUPRA TASTATURII (iOS).
   Bara e `position: fixed; bottom: 0` cu
   `padding-bottom: calc(12px + env(safe-area-inset-bottom))`. iOS urcă singur
   elementele fixate deasupra tastaturii — dar continuă să raporteze inset-ul de
   jos (bara de gesturi, ~34px), deși cu tastatura deschisă acolo nu mai e nicio
   bară de gesturi. Rezultatul e un spațiu gol între casetă și tastatură.
   Cât timp se scrie, renunțăm la rezerva aceea; revine la pierderea focusului.
   ------------------------------------------------------------------------ */
.up5-chat .search-bar-container:has(textarea:focus),
.up5-chat .search-bar-container:has(input:focus) {
  padding-bottom: 0.75rem;
}

/* ---------------------------------------------------------------------------
   ÎNĂLȚIMEA DE ECRAN: `svh`, nu `vh`.
   -----------------------------------------------------------------------------
   `framework.bundle.js:1585` scrie `--screen-height = window.innerHeight + 'px'`
   ca stil INLINE pe `<html>`. Pe telefon asta e înălțimea cu bara de adrese
   retrasă — adică viewportul MARE. Containerele dimensionate cu ea ies mai înalte
   decât zona vizibilă, deci ultimele mesaje cad sub margine și par tăiate; ca să
   le vezi trebuie să derulezi tu, deși n-ai făcut nimic care să ceară asta.
   Exact ce se raportează la primul mesaj trimis.

   `svh` e viewportul MIC (cu bara de adrese pe ecran) — cel garantat vizibil.
   Am ales `svh`, nu `dvh`, tocmai ca să NU se redimensioneze sub degete în timp
   ce derulezi: valoarea rămâne fixă, layoutul nu tresare.

   `!important` e necesar și suficient: o declarație importantă dintr-o foaie bate
   un stil inline neimportant, deci nu trebuie atins framework-ul, care e folosit
   de tot site-ul. Prima linie e rezerva pentru browserele fără `svh` — acolo a
   doua declarație e ignorată și rămâne comportamentul de azi.
   ------------------------------------------------------------------------ */
:root {
  --screen-height: 100vh !important;
  --screen-height: 100svh !important;
}

/* ---------------------------------------------------------------------------
   AVATARUL DIN HERO — halou tăiat pe iOS/Safari.
   `chat-v7.css:1481` folosește `filter: drop-shadow(0 10px 30px …)`. `filter` cere
   o suprafață proprie de randare, iar WebKit îi calculează marginile din cutia
   elementului: haloul coboară 40px (10 offset + 30 blur) sub un element de 73px,
   iar pe `display: inline-flex`, într-un context de linie, partea de jos e tăiată.
   Pe Blink nu se vede, fiindcă acolo regiunea filtrului e extinsă altfel.

   `drop-shadow` are sens când umbra trebuie să urmeze transparența conținutului.
   Aici nu e cazul: elementul e un CERC OPAC — fundal în gradient, `border-radius:
   50%`, cu o imagine care îl umple. `box-shadow` urmează raza de colț și dă exact
   același rezultat vizual, fără suprafață de filtru și fără nimic de tăiat.
   ------------------------------------------------------------------------ */
.acx-welcome .acx-w-av {
  filter: none;
  box-shadow: 0 0.625rem 1.875rem rgba(124, 92, 252, 0.35);
}

/* ---------------------------------------------------------------------------
   TASTATURA PE MOBIL — REZOLVATĂ ÎN chat-fix10.css (rama), NU AICI.
   -----------------------------------------------------------------------------
   Aici au stat, pe rând: modelul „în flux" (LP), app-shell-ul cu scroller
   intern, pre-strângerea și lacătul de gesturi — toate demontate. A urmat
   pariul „nativ" pe meta-ul `interactive-widget=resizes-content` (20 aug) —
   INFIRMAT: WebKit/iOS nu implementează cheia (bug 259770 deschis; consola o
   ignoră explicit). Meta-ul rămâne emis (ajută Chrome/Firefox Android), dar pe
   iOS tastatura e rezolvată de RAMA din chat-fix10.css §1 (`html.acx-rama`,
   pusă de twig înainte de primul desen) + driverul chat-fix10.js. Fără JS,
   bara cade pe definiția v7 `fixed; bottom: 0` (chat-v7.css:2800) — degradarea
   documentată a pachetului CHAT-10.

   NU reintroduce aici: poziționare statică a barei, min-height-uri pe listă,
   transform-uri sau variabile de înălțime proprii — istoria completă a
   eșecurilor e în memoria proiectului (chat-tastatura-mobil).
   ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
   BLOCAJUL COMPOZITORULUI CÂT TIMP SE SCRIE RĂSPUNSUL (20 aug, cerut explicit).
   -----------------------------------------------------------------------------
   De la trimiterea mesajului și până când răspunsul e gata, inputul NU e
   disponibil: ușor stins și fără pointer-events — nu poate primi tap, deci
   nici focus, deci tastatura nu se poate deschide peste streaming (exact
   scenariul care producea toate glitch-urile din v7/v8).

   Starea vine de la MOTOR, fără niciun cod nou: în timpul rulării există
   fie `.button-loading[data-active]` (se așteaptă răspunsul), fie
   `.button-stop[data-active]` (răspunsul curge) — dashboard.module.js le
   pune și le scoate pe toate drumurile (trimitere, streaming, stop, eroare).

   Butonul de STOP rămâne apăsabil ORICÂND: doar textarea pierde
   pointer-events; `.actions-cnt` (care ține stop-ul) nu e atins.
   Închiderea tastaturii la trimitere (blur) o face chat-shell.js — CSS-ul nu
   poate retrage un focus deja existent.
   ------------------------------------------------------------------------ */
/* BLOCAJUL COMPOZITORULUI — REACTIVAT la cerere (25 aug, seara).
   La trimitere: tastatura coboară (blur-ul din chat-shell.js), textarea capătă
   un mic fade și pierde pointer-events — imposibil de apăsat până nu termină
   asistentul de scris. STOP-ul rămâne apăsabil oricând (e în `.actions-cnt`,
   neatins de regulile de mai jos, care țintesc DOAR textarea). */
.up5-chat.js-chat-room .search-bar.js-chat-form textarea {
  transition: opacity 0.25s ease;
}
/* fade-ul VIZIBIL e al pastilei (textarea are fundal transparent — opacitatea
   ei singură abia se vede): fundalul și conturul pastilei se sting și ele,
   cu aceeași tranziție. `.actions-cnt` (stop-ul) NU e atins. */
.up5-chat.js-chat-room .search-bar.js-chat-form .input-bar {
  transition:
    background-color 0.25s ease,
    border-color 0.25s ease,
    box-shadow 0.25s ease;
}
/* Selectorul PRINCIPAL e clasa `acx-ruleaza`, pusă/scoasă de chat-shell.js pe
   formular după starea motorului — o clasă simplă nu depinde de suportul lui
   `:has()` pe Safari-ul telefonului. Regula pe `:has` stă SEPARAT (nu în
   aceeași listă): un selector neparsabil invalidează întreaga listă în care
   stă, deci gruparea ar fi omorât și clasa exact pe browserele pentru care
   există plasa. */
.up5-chat.js-chat-room .search-bar.js-chat-form.acx-ruleaza textarea {
  pointer-events: none;
  opacity: 0.35;
  /* indisponibil = ZERO reacție la tap, nu blocare vizibilă */
  user-select: none;
  -webkit-user-select: none;
}
.up5-chat.js-chat-room .search-bar.js-chat-form.acx-ruleaza .input-bar {
  background-color: rgba(13, 17, 23, 0.45);
  border-color: rgba(255, 255, 255, 0.05);
  box-shadow: none;
  cursor: default;
  -webkit-tap-highlight-color: transparent;
}
.up5-chat.js-chat-room .search-bar.js-chat-form.acx-ruleaza .input-bar::before,
.up5-chat.js-chat-room .search-bar.js-chat-form.acx-ruleaza .input-bar::after {
  opacity: 0;                       /* aurele/glow-ul pastilei se sting și ele */
}
/* plasă fără JS: aceeași regulă, dedusă direct din starea motorului */
.up5-chat.js-chat-room .search-bar.js-chat-form:has(.button-loading[data-active]) textarea,
.up5-chat.js-chat-room .search-bar.js-chat-form:has(.button-stop[data-active]) textarea {
  pointer-events: none;
  opacity: 0.35;
  user-select: none;
  -webkit-user-select: none;
}

/* ---------------------------------------------------------------------------
   MODUL DE SCRIERE (voal + camp marit + pozitionare) — ȘTERS COMPLET (25 aug).
   Cerinta finala, dupa 20 de variante: ZERO interventie pe textarea/tastatura.
   Comportamentul e integral al browserului + rama existenta (chat-fix10).
   NU reconstrui nimic din: voal, crestere la focus, bottom din visualViewport,
   inghet de container, istorie pentru back. */

/* „disclaimerul e usor intr-un scroll — containerul, nu continutul":
   MASURAT +16px pe `grid-x` in starea de disclaimer. Containerul nu are ce
   derula (caseta are propriul scroll interior); surplusul e sub rama casetei,
   in zona barei de acord — il taiem. (Al doilea selector: `html.acx-rama ...
   { overflow-y: auto }` din blocul ramei ar invinge primul — masurat.) */
@media (max-width: 1023.98px) {
  .up5-chat > .messages > .grid-x:has(.message-assistant-disclaimer),
  html.acx-rama .up5-chat > .messages > .grid-x:has(.message-assistant-disclaimer) {
    overflow-y: hidden;
  }
}

/* ---------------------------------------------------------------------------
   VOALUL DE SCRIERE + BLOCAJUL DE SCROLL (25 aug, cerut pe baza curată).
   -----------------------------------------------------------------------------
   Singurele două intervenții permise peste v7-ul pur: la focus pe câmpul de
   mesaj (cu gest real), restul paginii intră sub un voal transparent foarte
   ușor blurat, iar PAGINA NU MAI POATE DERULA în nicio direcție cât timp
   voalul e jos. NIMIC din geometria barei sau a textarei nu e atins — poziția
   lor cu tastatura deschisă rămâne cea nativă.

   Voalul stă în `.up5acc` (context de stivuire propriu, z-index: 2): înăuntru,
   55 îl așază peste header (40) și conținut, dar SUB bara compozitorului (60).
   Ieșiri (chat-patch2.js, modVoal): tap în afara câmpului, coborârea
   tastaturii, butonul/gestul de înapoi. */
@media (max-width: 1023.98px) {
  .acx-voal-scriere {
    /* ABSOLUTE, nu fixed — lecția finală a fâșiei: cu tastatura sus, iOS ridică
       TOT stratul de elemente fixed deasupra tastaturii+pastilei de URL, deci
       orice voal fixed e tăiat la aceeași margine ca bara, oricât de întins;
       fâșia de sub ele (unde se vedea cardul de produs) e desenată din stratul
       DOCUMENTULUI. Un voal absolute cu inset:0 în `.up5acc` acoperă întregul
       document, la orice scroll — inclusiv fâșia. Pastila de URL în sine e
       chrome de browser și nu poate fi acoperită de nimeni. */
    position: absolute;
    inset: 0;
    /* `svh`, NU `vh` (26 aug, videoul cu „scroll complet nenecesar"): voalul e
       montat PERMANENT (opacity 0 — tranziția cere element persistent), iar un
       absolute își extinde zona derulabilă a strămoșului poziționat. Pe iPhone
       100vh = viewportul MARE (fără pastila de URL), deci `min-height: 100vh`
       ținea documentul cu ~60-70px mai înalt decât pagina reală (100svh) — în
       ORICE stare a chatului, „scroll mereu posibil". Dovedit pe emulator prin
       simulare: min-height 913 pe voal → scrollHeight 913. `inset: 0` singur
       acoperă oricum tot documentul (up5acc ≥ 100svh prin up5acc-content);
       plafonul svh e doar plasa pentru un document mai scund decât ecranul. */
    min-height: 100vh; /* rezervă pentru browsere fără svh */
    min-height: 100svh;
    z-index: 55;
    background: rgba(8, 11, 18, 0.28);
    -webkit-backdrop-filter: blur(0.15625rem);
    backdrop-filter: blur(0.15625rem);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.22s ease-out;
  }
  body.acx-scriere .acx-voal-scriere {
    opacity: 1;
    pointer-events: auto;
  }
  /* FĂRĂ `overflow: hidden` aici — lecția v6: lacătul făcea „ilegal" chiar
     reveal-ul nativ al lui Safari (singurul care așază corect bara deasupra
     tastaturii — vizibil în v6 înainte să intre lacătul) și totul aluneca.
     Scroll-ul UTILIZATORULUI e blocat de voal, care consumă toate gesturile
     (pointerdown/touchmove/wheel, în chat-patch2.js); al browserului rămâne
     liber, pentru că el ține textarea jos. */
}
@media (prefers-reduced-motion: reduce) {
  .acx-voal-scriere { transition: none; }
}

/* draftul lung derulează DOAR în textarea; la capete nu se înlănțuie în pagină */
@media (max-width: 1023.98px) {
  body.acx-scriere .up5-chat .input-bar textarea {
    overscroll-behavior: contain;
  }
}

/* ---------------------------------------------------------------------------
   INTRAREA PLANULUI: nu aici. Aici a stat o zi (25 aug) un fade inventat
   (`acxPlanIntra`) — respins: nu asta era animația pierdută. Cea ORIGINALĂ
   (v6.7: cortina „Generez planul tău…" + dezvăluirea blur→clar `acxGenIn`)
   trăiește întreagă în chat-v7.css:2283-2385 și e re-armată din
   chat-patch2.js (clasa `chat-meal-plan` pusă înapoi pe `.message` — vezi
   nota de la impacheteazaTyping). CSS-ul de aici adaugă doar REGIA CARDULUI
   de mai jos. */

/* ---------------------------------------------------------------------------
   REGIA CARDULUI DE CUMPĂRARE (25 aug, §H din chat-patch2.js): cât timp planul
   „se generează" (cortina), cardul cu link-ul spre program stă STRÂNS, nu doar
   transparent — altfel derularea la fundul mesajului ateriza pe o zonă goală
   de sute de pixeli și scotea cortina din viewport. `max-height: 0` +
   `overflow: hidden` țin pagina scurtă; interiorul rămâne AȘEZAT (nu e
   display:none), deci click-ul automat al motorului pe acordeonul de plan
   (dashboard.module.js, setTimeout la 1s după clonare) măsoară înălțimi reale.
   La dezvăluirea planului clasa se schimbă și cardul intră animat. */
.up5-chat .message-container .event-purchase-plan.acx-card-ascuns {
  max-height: 0;
  opacity: 0;
  /* `visibility` e necesar PE LÂNGĂ opacity: intrarea proprie a cardului
     (`acxCardIn`, chat-v7) animă opacity și, fiind animație, bate cascada —
     fără visibility rămânea un fir de 2px complet opac */
  visibility: hidden;
  overflow: hidden;
  margin: 0;
  pointer-events: none;
}
/* scroll anchoring-ul browserului nu are voie să reacționeze la creșterea
   cardului (max-height 0 → ~500px la intrare): planul pe două zile rămâne
   exact unde e în viewport. Dublează ancorarea din JS (§H, `arata`). */
.up5-chat .message-container .event-purchase-plan {
  overflow-anchor: none;
}

/* ---------------------------------------------------------------------------
   §I. CADRUL DE GENERARE (27 aug) — bula cu cortina care rulează cât durează
   generarea reală pe server (chat-patch2.js, montezaCadruGenerare).
   Structura .message--assistant.chat-meal-plan cu un .chat-meal-plan gol
   aprinde singură cortina v6.7 din chat-v7.css; aici doar dăm lățime cutiei
   (mesajul cu un singur rând de text s-ar strânge pe conținut și cortina ar
   ieși un fir vertical) și ascundem bula de puncte a motorului cât timp
   cadrul e pe ecran — două „aștept"-uri simultan ar fi zgomot. */
.up5-chat .message-container[data-acx-gen-frame] {
  width: 100%;
}
.up5-chat [data-acx-gen-frame] .message {
  width: 100%;
}
.up5-chat .js-messages-container:has([data-acx-gen-frame]) .message-container:has(.message--pending) {
  display: none;
}

/* ---------------------------------------------------------------------------
   §H. CONVERSIA — mesaj separat: bula-titlu + cardul, intră împreună, o
   singură mișcare (containerul animă; animația proprie a cardului e oprită
   aici ca să nu se suprapună două urcări). */
.up5-chat .message-container.acx-conversie {
  width: 100%;
  animation: acxCardIntra 0.55s var(--ease-emph, ease-out) both;
}
.up5-chat .acx-conversie .event-purchase-plan.acx-card-intra {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .up5-chat .message-container.acx-conversie { animation: none; }
}

/* ---------------------------------------------------------------------------
   Mini-formularul de plan (chat-tools-v7.js, miniaturizeazaFormular): linia
   „✓ Am preluat: …" de deasupra celor două întrebări rămase. */
.up5-chat .event-user-form .nt-mini-preluat {
  width: 100%;
  font-size: 0.84375rem;
  line-height: 1.5;
  color: rgba(230, 236, 245, 0.72);
  background: rgba(1, 219, 105, 0.07);
  border: 0.0625rem solid rgba(1, 219, 105, 0.22);
  border-radius: 0.625rem;
  padding: 0.5rem 0.75rem;
}
.up5-chat .event-user-form .nt-mini-preluat b {
  color: #01db69;
  font-weight: 600;
}
.up5-chat .message-container .event-purchase-plan.acx-card-intra {
  animation: acxCardIntra 0.55s var(--ease-emph, ease-out) both;
}
@keyframes acxCardIntra {
  from { opacity: 0; transform: translateY(1rem); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .up5-chat .message-container .event-purchase-plan.acx-card-intra { animation: none; }
}

/* Cerut 7 sept (Dennis): butonul de trimitere din compozitorul chatului −15%
   pe desktop (46→39px). `.actions-cnt` e cutia comună — send/stop/loading
   stau toate cu inset: 0 în ea, deci scad împreună, cum trebuie. */
@media (min-width: 1024px) {
  /* bara aliniază jos (textarea crește multiline) — la 46px butonul pica
     întâmplător pe centru, la 39px rămânea cu 4px prea jos; margin-bottom îl
     recentrează pe starea de un rând și îl ține tot jos-ancorat la multiline.
     margin-right 2px: insetul din dreapta egal cu cel vertical (~8px). */
  .up5-chat .actions-cnt { flex: 0 0 2.4375rem; width: 2.4375rem; height: 2.4375rem; margin-bottom: 0.25rem; margin-right: 0.125rem; }
}

/* Cerut 7 sept (Dennis): numerele din răspunsurile text ale asistentului
   (chat-numere.js) - bold + gradientul site-ului (.font-gradient). Numărul și
   unitatea lui nu se despart la capăt de rând; padding-ul mic ține descendentele
   unităților („kg") în interiorul decupajului background-clip:text. */
.up5-chat .message--assistant .acx-num {
  white-space: nowrap;
  font-weight: 700;
  color: #fff; /* 10 sept: doar bold, alb - fără gradient */
}

/* Cerut 7 sept (Dennis): pictogramele din compozitor (send / loading / stop)
   FĂRĂ fundal și mai îngroșate. Fundalul e o formă din SVG (cercul-gradient),
   nu un stil - îl ascundem pe primul element al fiecărei pictograme; liniile
   subțiri ale săgeții și spițele spinner-ului primesc contur alb mai gros.
   Fără disc, pictograma ocupă doar centrul cutiei, deci o mărim puțin ca să
   umple vizual același loc; zona de apăsare rămâne cutia întreagă. */
.up5-chat .actions-cnt button[type="submit"] svg > rect:first-child,
.up5-chat .actions-cnt .button-loading svg > path:first-child {
  display: none;
}
.up5-chat .actions-cnt button[type="submit"] svg g path {
  /* corectat 7 sept („mult prea accentuată"): fără mărire, contur cu 25% mai
     subțire (2.4 → 1.8) */
  stroke: #fff;
  stroke-width: 1.8;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.up5-chat .actions-cnt .button-loading svg path {
  stroke: #fff;
  stroke-width: 1.2;
  stroke-linejoin: round;
}
.up5-chat .actions-cnt .button-loading svg {
  transform: scale(1.12);
  transform-origin: 50% 50%;
}
.up5-chat .actions-cnt .button-stop svg {
  transform: scale(1.12);
  transform-origin: 50% 50%;
}
.up5-chat .actions-cnt button[type="submit"]:hover {
  filter: drop-shadow(0 0 0.625rem rgba(124, 92, 252, 0.55));
}
