La Fleur

Ô long Sữa Hoa nhài

Mang hương nhài trắng, vị tươi nhẹ và hậu béo sữa dịu dàng.
Thức uống này được phục vụ với hai kích cỡ
Order on GrabFood 45,000₫
/* Tư Trà — Price flip animation (scoped to your button markup) */
.tt-price-anim .tu-tra-live-price.tt-price-flip{
  display:inline-block;
  position:relative;        /* anchor overlay */
  vertical-align:baseline;
}

/* overlay wrapper injected only during the flip */
.tt-price-anim .tu-tra-live-price .ttpf-wrap{
  position:absolute;
  inset:0;                  /* left:0; top:0; right:0; bottom:0 */
  overflow:hidden;
  pointer-events:none;
}

/* two lines that slide */
.tt-price-anim .tu-tra-live-price .ttpf-line{
  position:absolute;
  left:0; right:0;
  will-change:transform, opacity;
  transform:translateY(0);
  opacity:1;
  transition:transform 360ms cubic-bezier(.22,.61,.36,1), opacity 360ms linear;
  text-align:inherit;
  white-space:nowrap;
}

/* enter positions */
.tt-price-anim .tu-tra-live-price .ttpf-line.new-up{ transform:translateY(100%); }  /* price ↑ enters from bottom */
.tt-price-anim .tu-tra-live-price .ttpf-line.new-dn{ transform:translateY(-100%); } /* price ↓ enters from top   */

/* phase classes on wrapper */
.tt-price-anim .tu-tra-live-price .ttpf-anim-up  .old{ transform:translateY(-100%); opacity:0; }
.tt-price-anim .tu-tra-live-price .ttpf-anim-up  .new{ transform:translateY(0%);    opacity:1; }
.tt-price-anim .tu-tra-live-price .ttpf-anim-dn  .old{ transform:translateY(100%);  opacity:0; }
.tt-price-anim .tu-tra-live-price .ttpf-anim-dn  .new{ transform:translateY(0%);    opacity:1; }

(function(){
  // Host (.tt-price-anim) → we bind/rebind ONLY inside these
  var HOST_SEL  = '.tt-price-anim';
  var PRICE_SEL = '.tu-tra-live-price, #tu-tra-live-price, [data-tu-tra-live-price], [data-dynamic-tag="tu_tra_live_price"], [data-brx-bind*="tu_tra_live_price"], [data-brx-content*="tu_tra_live_price"]';

  function parsePrice(text){
    if(!text) return null;
    var t = String(text).replace(/\u00A0/g,' ').trim();
    if(!t || /không có/i.test(t)) return null;
    var digits = t.replace(/[^0-9]/g,'');
    if(!digits) return null;
    var n = parseInt(digits,10);
    return Number.isFinite(n) ? n : null;
  }

  function flipOnce(el, oldText, newText){
    if(oldText === newText) return;
    if(el.__ttpfBusy) { el.__ttpfPrev = newText; return; }
    el.__ttpfBusy = true;

    var wrap = document.createElement('span');
    wrap.className = 'ttpf-wrap';
    var lineOld = document.createElement('span');
    var lineNew = document.createElement('span');
    lineOld.className = 'ttpf-line old';
    lineNew.className = 'ttpf-line new';

    var oldN = parsePrice(oldText);
    var newN = parsePrice(newText);
    var up = true;
    if(oldN !== null && newN !== null) up = newN > oldN;
    lineNew.classList.add(up ? 'new-up' : 'new-dn');

    lineOld.textContent = oldText;
    lineNew.textContent = newText;

    // lock width/height to avoid jiggle during flip
    var rect = el.getBoundingClientRect();
    if(rect.width)  el.style.minWidth  = rect.width  + 'px';
    if(rect.height) el.style.minHeight = rect.height + 'px';

    wrap.appendChild(lineOld);
    wrap.appendChild(lineNew);
    el.appendChild(wrap);

    // set final text underneath (so DOM is correct during/after anim)
    el.textContent = newText;

    // kick animation next frame
    requestAnimationFrame(function(){
      wrap.classList.add(up ? 'ttpf-anim-up' : 'ttpf-anim-dn');
    });

    // cleanup
    var cleaned = false;
    function done(){
      if(cleaned) return; cleaned = true;
      if(wrap.parentNode) wrap.parentNode.removeChild(wrap);
      el.style.minWidth = el.style.minHeight = '';
      el.__ttpfPrev = newText;
      el.__ttpfBusy = false;
    }
    wrap.addEventListener('transitionend', done, { once:true });
    setTimeout(done, 650); // safety
  }

  function bindPrice(el){
    if(!el) return;
    if(el.__ttpfBound) return;
    el.__ttpfBound = true;
    el.__ttpfPrev = (el.textContent || '').trim();
    el.__ttpfBusy = false;
    el.classList.add('tt-price-flip');

    // Observe this price span for text changes; coalesce via RAF
    var mo = new MutationObserver(function(){
      if(!el.isConnected){ try{mo.disconnect();}catch(e){} return; }
      if(el.__ttpfRaf) cancelAnimationFrame(el.__ttpfRaf);
      el.__ttpfRaf = requestAnimationFrame(function(){
        var curr = (el.textContent || '').trim();
        var prev = el.__ttpfPrev || '';
        if(curr !== prev) flipOnce(el, prev, curr);
      });
    });

    try{
      mo.observe(el, { characterData:true, childList:true, subtree:true });
      el.__ttpfMO = mo;
    }catch(e){ /* ignore */ }
  }

  function bindHost(host){
    if(!host || !host.isConnected) return;
    // (re)bind price element inside this host
    var priceEl = host.querySelector(PRICE_SEL);
    if(priceEl) bindPrice(priceEl);

    // host-level observer: if the price span is replaced, bind again
    if(host.__ttpfHostMO) return;
    var hostMO = new MutationObserver(function(muts){
      // only react if a price element appears or is replaced inside host
      if(!host.isConnected){ try{hostMO.disconnect();}catch(e){} return; }
      var el = host.querySelector(PRICE_SEL);
      if(el && !el.__ttpfBound) bindPrice(el);
    });
    try{
      hostMO.observe(host, { childList:true, subtree:true });
      host.__ttpfHostMO = hostMO;
    }catch(e){ /* ignore */ }
  }

  function initAll(){
    document.querySelectorAll(HOST_SEL).forEach(bindHost);
  }

  // Initial + late load safety
  if(document.readyState === 'loading'){
    document.addEventListener('DOMContentLoaded', initAll, { once:true });
  } else {
    initAll();
  }
  window.addEventListener('load', initAll, { once:true });

  // Optional: small, filtered page observer to catch hosts added later by Bricks
  var pageMO = new MutationObserver(function(muts){
    for (var i=0;i<muts.length;i++){
      var m = muts[i];
      for (var j=0;j<m.addedNodes.length;j++){
        var n = m.addedNodes[j];
        if(!(n instanceof Element)) continue;
        if(n.matches && n.matches(HOST_SEL)) bindHost(n);
        var sub = n.querySelectorAll ? n.querySelectorAll(HOST_SEL) : [];
        if(sub.length) sub.forEach(bindHost);
      }
    }
  });
  try{
    pageMO.observe(document.body, { childList:true, subtree:true });
  }catch(e){ /* ignore */ }
})();

Chiều muộn dịu dàng trên vùng trà Bảo Lộc. Những cơn gió nhẹ lướt qua cánh đồng hoa nhài ven đồi, mang theo hương thơm thanh khiết lan khắp không gian tĩnh lặng.

Trà Ô Long được ủ cùng cánh hoa nhài tươi, sau đó chiết xuất bằng phương pháp ủ nóng chậm, hoà quyện với lớp kem sữa mịn màng — tạo nên một tổng thể nhẹ tênh nhưng đầy cuốn hút.

Ô Long Sữa Nhài mang đến cảm giác trong trẻo như sương sớm, thoảng mùi hoa trắng, dịu mát như bóng râm ngày hạ, và hậu vị mềm mại vương lại trên đầu lưỡi.

Một bản giao hưởng tinh khôi giữa thiên nhiên và thời gian — tĩnh tại, thư thả, gợi nhắc về sự giản đơn nhưng đầy chiều sâu của những phút nghỉ ngơi thật sự.