* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  font-size: 16px; /* bază pentru toate valorile em din visual.json (style.rootFontSize) */
}

html, body {
  width: 1920px;
  height: 1080px;
  overflow: hidden;
  background: #000;
}

/* culoarea bulei de preț — variabile CSS, ca să poată fi schimbate instant
   (fără alt SVG) dintr-un singur loc; price-bubble.svg are fill ca atribut
   simplu pe cele 2 <path> (corp + "coadă"/umbră), nu style inline, deci orice
   regulă CSS le suprascrie fără probleme */
:root {
  --bubble-fill: #ad1640;
  --bubble-fill-shadow: #7f1133;
}
.price-bubble svg path:nth-of-type(1) { fill: var(--bubble-fill); }
.price-bubble svg path:nth-of-type(2) { fill: var(--bubble-fill-shadow); }

/* variantă test: auriu #FCD116 (cerere client) — activă DOAR când clasa e
   pusă pe <body>, din panoul de test (?testBubble=1 în URL, vezi engine.js).
   Umbra e o aproximare (păstrează același raport de întunecare ca originalul
   ad1640→7f1133); de fin-reglat cu Grafica dacă varianta asta e aleasă final.
   Textul din bulă (preț) e alb fix (din visual.json) — pe auriu deschis
   alb-pe-alb nu se mai vede, deci comutăm și noi la o culoare închisă doar
   pentru această variantă (!important, ca să bată stilul inline din JS). */
body.bubble-variant-gold {
  --bubble-fill: #FCD116;
  --bubble-fill-shadow: #C2A111;
}
body.bubble-variant-gold .price-bubble .bubble-wp,
body.bubble-variant-gold .price-bubble .bubble-price {
  color: #ab163f !important;
}

/* --- panou de test culoare bulă — DOAR cu ?testBubble=1, altfel nu există
   deloc în DOM. Apărut SUB layout-ul de 1920×1080 (nu suprapus), de-aia
   permitem overflow/înălțime liberă pe html/body doar în acest mod. Complet
   inofensiv pentru board-ul de producție (fără query param). */
html.test-mode, html.test-mode body {
  height: auto;
  min-height: 1080px;
  overflow: visible;
}
#bubble-test-panel {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: #111;
  color: #fff;
  font-family: Arial, sans-serif;
  font-size: 15px;
}
#bubble-test-panel button {
  padding: 8px 16px;
  border-radius: 6px;
  border: 1px solid #555;
  background: #222;
  color: #fff;
  cursor: pointer;
  font-size: 14px;
}
#bubble-test-panel button.active {
  outline: 2px solid #fff;
  font-weight: bold;
}

#board {
  position: relative;
  width: 1920px;
  height: 1080px;
  overflow: hidden;
}

#bg {
  position: absolute;
  top: 0; left: 0;
  width: 1920px;
  height: 1080px;
  /* fără z-index: ordinea (bg sub, menus deasupra) vine din ordinea din DOM.
     z-index aici ar crea un context de stivuire izolat, care ar bloca
     mix-blend-mode al umbrelor produselor să "vadă" fundalul */
}

#menus {
  position: absolute;
  top: 0; left: 0;
  width: 1920px;
  height: 1080px;
}

.menu {
  position: absolute;
  top: 0;
  width: 640px;
  height: 1080px;
}
.menu[data-col="0"] { left: 0px; }
.menu[data-col="1"] { left: 640px; }
.menu[data-col="2"] { left: 1280px; }

.menu-title {
  position: absolute;
  top: 50px;
  width: 100%;
  text-align: center;
  text-transform: uppercase;
}
.menu-title .divider {
  display: block;
  width: 400px;
  height: auto; /* proporțional, păstrează raportul nativ al SVG-ului (400×21.15) */
  margin: 14px auto 0;
}

.pepsi-logo {
  position: absolute;
  bottom: 50px;
  left: 40px;
  /* width/height vin din CONTROL.pepsiLogo (visual.json), setate inline din JS */
  z-index: 5;
  transition: opacity .4s ease;
}
.pepsi-logo.hidden { opacity: 0; visibility: hidden; }

/* --- product stack --- */
.product {
  position: absolute;
  /* x,y din visual.json reprezintă CENTRUL imaginii, nu colțul stânga-sus —
     centrarea se face acum în JS (centerProductOnLoad), NU prin transform:
     transform crea un context nou de stivuire care izola mix-blend-mode al
     umbrei de fundalul real (bg3.jpg), deci multiply nu se vedea deloc */
}

/* prop static sub un produs (ex: napkin.png) — fără flicker/pop, aceeași
   centrare JS ca .product, fără mix-blend-mode (nu are nevoie de multiply) */
.product-prop {
  position: absolute;
}
.product-prop .prop-img {
  display: block;
}

/* umbra: pentru produse fără variantă alt e element static separat, mereu
   la opacitate 1. Pentru produse cu variantă alt sunt DOUĂ straturi
   persistente (.shadow-main/.shadow-alt, vezi buildProductEl), care fac
   flicker sincron cu imaginea și pop-in la schimbare — dar TOATĂ animația
   (opacitate + transform) e scrisă discret din JS (runFlickerSteps,
   runShadowPopIn), NU prin @keyframes CSS, ca să nu izoleze mix-blend-mode
   pe durata tranziției */
.product-shadow {
  display: block;
  position: absolute;
  top: 0; left: 0;
  z-index: 1;
  opacity: 1;
  mix-blend-mode: multiply;
  transform-origin: 50% 50%;
}

/* straturile de imagine (bază/alt) — doar fotografia produsului, fără
   umbră. layer-main e mereu deasupra; ordinea vine din DOM (layer-alt
   construit înaintea lui layer-main), NU din z-index — z-index aici ar
   crea un context de stivuire care ar izola umbra de fundal */
.img-layer img {
  display: block;
  position: relative;
  z-index: 2;
}
.layer-main {
  position: relative;
  opacity: 1;
}
.layer-alt {
  position: absolute;
  top: 0; left: 0;
  opacity: 0;
}

/* flicker: opacitatea straturilor se scrie direct din JS (runFlickerSteps),
   NU prin @keyframes CSS — o animație CSS pe "opacity" ar promova elementul
   într-un strat izolat pe toată durata ei, rupând mix-blend-mode al umbrei */

/* pop pe imagine — DOAR pe fotografia produsului (main-layer); umbra e
   element separat și dispare/reapare via JS, nu prin această animație */
.img-layer.popping img.main-layer {
  animation: productPop .35s ease-out;
  transform-origin: 50% 50%;
}
@keyframes productPop {
  0%   { transform: scale(1); }
  55%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}

/* --- product text stack: un singur container, poziția lui (top/left) vine
   din menu.productsTextPosition (JSON), independent de titlu; în interior
   blocurile se stivuiesc în flux normal (fiecare ia exact spațiul necesar) --- */
.product-text-stack {
  position: absolute;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.product-info {
  text-align: center;
  margin-bottom: 0;
}
.product-info .or-label {
  display: block;
  margin: 2px 0;
}
.product-info .p-name { display: inline; }
.product-info .p-qty { margin-left: 6px; }
.product-info .p-type { display: block; }

/* fiecare variantă (bază/alt) e bloc separat, ca să poată face pop individual */
.variant-block {
  display: block;
  transform-origin: 50% 50%;
}

/* "+" e separator între produse, în flux normal — nu face niciodată pop
   (nici la reset de limbă, nici la flicker de imagine) */
.connector {
  display: block;
  margin: 0;
  text-align: center;
  line-height: 1.1;
}

.text-group.popping,
.variant-block.popping {
  animation: textPop .35s ease-out;
}
@keyframes textPop {
  0%   { transform: scale(1); }
  55%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}

/* --- price bubble --- */
.price-bubble {
  position: absolute;
  z-index: 6;
}
/* .bubble-inner wraps SVG + text together so the whole bubble moves as one unit.
   Starea DEFAULT (fără nicio animație activă) e scale 0 / rotate 45° —
   starea "înainte de pop-in", cerută explicit ca punct de plecare la
   începutul fiecărei secvențe ro-RO (nu doar prin animation fill-mode, ca
   să fie corectă și la primul paint, înainte ca JS să apuce să pornească
   vreo animație). NUMAI transform (scale/rotate) — FĂRĂ opacitate: scale(0)
   e deja suficient ca bula să fie invizibilă (element de dimensiune 0), nu
   se ating opacitatea nici la pop-in, nici la pop-out. */
.price-bubble .bubble-inner {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  transform-origin: 50% 50%;
  transform: scale(0) rotate(45deg);
}
.price-bubble svg {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}
.price-bubble .bubble-text {
  position: relative;
  z-index: 1;
  text-align: center;
  transform: translateY(-0.75em); /* bula are o "coadă" jos, centrul vizual e mai sus decât centrul geometric */
}
.price-bubble .bubble-wp { display: block; text-decoration: line-through; }
.price-bubble .bubble-price { display: block; }

/* Efect "gelatinos", TREI animații separate pe același element (comma-list),
   înlănțuite via delay (fiecare pornește exact când se termină precedenta):
   1) bubblePopIn — intrarea (scale+rotate, RAPID: 500ms fix, NU proporțional
      cu durata totală). De la 0% (invizibilă, scale 0, rotită 45°) la 100%
      (poziție normală: scale 1, rotate 0). fill-mode "both" ține starea
      scale 0/rotate 45° și ÎNAINTE să înceapă (cât timp bula așteaptă
      propriul jellyDelay, dacă are unul) — cerință explicită: la începutul
      secvenței ro-RO, până la pop-in, bula trebuie să fie scale 0/rotate 45°.
   2) bubbleJellySquash — pornește exact când se termină pop-in-ul, NU mai e
      scale+rotate, ci proporții (scaleX/scaleY în opoziție, ca un balon care
      se înalță și se turtește pe rând), cu amplitudine ce scade treptat dar
      NU ajunge niciodată la scaleX=scaleY=1 exact — rămâne mereu un mic
      reziduu de mișcare.
   3) bubblePopOut — exact inversul lui bubblePopIn (scale 1→0, rotate 0→45°),
      ACEEAȘI durată (500ms), programat să se termine EXACT la finalul
      secvenței en-GB (finalul ciclului de limbă) — bula dispare chiar când
      se termină ultima limbă din cycle, înainte ca ro-RO să repornească.
   Durata celei de-a doua animații (bubbleJellySquash) e calculată dinamic
   din JS (triggerBubbleJelly) ca (tl×cycle.length − jellyDelay − 0.5s×2),
   ca ansamblul întreg (delay + pop-in + wiggle + pop-out) să acopere exact
   un ciclu complet de limbă pentru FIECARE bulă, indiferent de jellyDelay —
   toate se termină (pop-out inclus) exact în același moment, la resetul
   următor. */
/* IMPORTANT: fill-mode "both" (backwards+forwards) e pus DOAR pe bubblePopIn
   — vrem ca starea ei 0% (scale 0/rotate 45°) să se aplice ȘI înainte să
   înceapă (cât timp așteaptă jellyDelay-ul). bubbleJellySquash și
   bubblePopOut au DOAR "forwards": dacă ar avea și "backwards", cu delay-uri
   mari (ex. popOut are delay ~19.5s dintr-un ciclu de 20s), fill-ul lor
   "backwards" le-ar face "în efect" ÎNCĂ DIN t=0 — și, fiind ULTIMELE din
   listă, ar câștiga în fața lui bubblePopIn pe toată durata delay-ului lor,
   afișând bula la scale(1) (100%, complet vizibilă) mult înainte să fie
   cazul. Cu doar "forwards", nu sunt "în efect" decât după ce încep efectiv
   — bubblePopIn rămâne vizibil/activ până atunci, exact cum trebuie. NICIO
   animație de aici nu atinge opacitatea (doar transform: scale/rotate) —
   scale(0) e deja suficient ca bula să dispară vizual. */
.price-bubble .bubble-inner.wizzing {
  animation-name: bubblePopIn, bubbleJellySquash, bubblePopOut;
  animation-timing-function: cubic-bezier(.34, 1.56, .64, 1), linear, cubic-bezier(.55, 0, .85, .35);
  animation-fill-mode: both, forwards, forwards;
  animation-iteration-count: 1, 1, 1;
}
@keyframes bubblePopIn {
  0%   { transform: scale(0) rotate(45deg); }
  100% { transform: scale(1) rotate(0deg); }
}
@keyframes bubblePopOut {
  0%   { transform: scale(1) rotate(0deg); }
  100% { transform: scale(0) rotate(45deg); }
}
@keyframes bubbleJellySquash {
  0%   { transform: scaleX(1) scaleY(1); }
  5%   { transform: scaleX(0.806) scaleY(1.24); }
  10%  { transform: scaleX(1.220) scaleY(0.82); }
  15%  { transform: scaleX(0.870) scaleY(1.15); }
  20%  { transform: scaleX(1.136) scaleY(0.88); }
  25%  { transform: scaleX(0.917) scaleY(1.09); }
  30%  { transform: scaleX(1.087) scaleY(0.92); }
  35%  { transform: scaleX(0.948) scaleY(1.055); }
  40%  { transform: scaleX(1.053) scaleY(0.95); }
  45%  { transform: scaleX(0.969) scaleY(1.032); }
  50%  { transform: scaleX(1.029) scaleY(0.972); }
  55%  { transform: scaleX(0.980) scaleY(1.020); }
  60%  { transform: scaleX(1.016) scaleY(0.984); }
  65%  { transform: scaleX(0.987) scaleY(1.013); }
  70%  { transform: scaleX(1.010) scaleY(0.990); }
  75%  { transform: scaleX(0.991) scaleY(1.009); }
  80%  { transform: scaleX(1.008) scaleY(0.992); }
  85%  { transform: scaleX(0.991) scaleY(1.009); }
  90%  { transform: scaleX(1.008) scaleY(0.992); }
  95%  { transform: scaleX(0.991) scaleY(1.009); }
  100% { transform: scaleX(1.006) scaleY(0.994); }
}

/* SGR-ul a fost scos din bulele de preț (era repetat de 3 ori) și mutat aici,
   O SINGURĂ DATĂ, deasupra disclaimerului — #footer-info e containerul comun,
   poziționat absolut; #sgr-line și #disclaimer stau în flux normal (block)
   unul sub altul în interiorul lui, fără calcule fragile de offset-uri */
#footer-info {
  position: absolute;
  bottom: 18px;
  left: 0;
  width: 1920px;
  text-align: center;
  z-index: 7;
}
#sgr-line {
  margin-bottom: 6px;
}
#sgr-line .i18n + .i18n {
  margin-left: 6px;
}
#disclaimer {
  /* poziționarea absolută veche a fost mutată pe #footer-info */
}
