/**
 * Tư-Trà Hero Header Suppress
 *
 * Keeps the header at full size while a "fullsize hero" section is
 * visible above the header. .scrolling only activates once the next
 * section's top crosses the bottom of the header — i.e., the moment
 * the next section actually starts sliding under the sticky header.
 *
 * Usage:
 *   In Bricks Builder, add the CSS class `tt-fullsize-hero` to the
 *   hero section. The script automatically pairs it with the next
 *   sibling section to detect when the hero region ends.
 *
 * Architecture:
 *   - Threshold: next-sibling.rect.top vs header.rect.bottom
 *     > 0  : hero region dominant — suppress .scrolling
 *     <= 0 : next section overlapping header — allow .scrolling
 *
 *   - Synchronous scroll listener (no rAF throttle): runs in the same
 *     event tick as Bricks's handler, so .scrolling is stripped before
 *     the browser paints. No frame-ordering race, no hesitation on iOS
 *     momentum scroll.
 *
 *   - MutationObserver on body + header class attribute: backup for
 *     when Bricks toggles .scrolling outside of scroll events (rAF,
 *     setTimeout, transitionend). Fires as a microtask, before paint —
 *     the briefly-added class is gone before the user sees it.
 *
 *   - Auto-no-op on pages without a hero (early return).
 *   - No CSS changes required.
 *
 * Version: 1.2.0
 * Author: Charlie + Claude
 *
 * History
 * v1.0.0: IntersectionObserver on hero. Broke for sticky heroes —
 *         IO reports the hero as intersecting for its entire stuck
 *         duration because the bounding rect stays at top:0.
 * v1.1.0: Switched to next-sibling rect check. Fixed sticky case.
 *         Worked on desktop, but rAF throttle exposed a frame-ordering
 *         race against Bricks's own rAF on iOS momentum scroll —
 *         visible hesitation when scrolling back to top.
 * v1.2.0: Synchronous scroll handler eliminates the rAF race.
 *         MutationObserver covers Bricks's non-scroll additions of
 *         .scrolling. Threshold raised from y=0 to header.bottom so
 *         shrink begins at the correct visual moment (next section
 *         meeting the header, not crossing the viewport top).
 */
(function () {
  'use strict';

  var HERO_SELECTOR   = '.tt-fullsize-hero';
  var SCROLLING_CLASS = 'scrolling';
  var HEADER_SELECTOR = '#brx-header, header.brxe-header';

  function init() {
    var heroes = document.querySelectorAll(HERO_SELECTOR);
    if (heroes.length === 0) return;

    // Pair each hero with its next sibling. Next sibling acts as the
    // visual signal for "hero region has ended" — works for both
    // sticky and non-sticky heroes.
    var pairs = [];
    for (var i = 0; i < heroes.length; i++) {
      pairs.push({ hero: heroes[i], next: heroes[i].nextElementSibling });
    }

    var headerEls = document.querySelectorAll(HEADER_SELECTOR);
    var heroActive = false;

    function isHeroActive() {
      // Read header bottom each call. The header may itself shrink
      // (changing its height) when .scrolling is on, but we only ever
      // need this measurement at the threshold moment, where the
      // header is still full-size.
      var headerBottom = 0;
      if (headerEls.length > 0) {
        headerBottom = headerEls[0].getBoundingClientRect().bottom;
      }
      for (var j = 0; j < pairs.length; j++) {
        var p = pairs[j];
        var endRef = p.next
          ? p.next.getBoundingClientRect().top
          : p.hero.getBoundingClientRect().bottom;
        if (endRef > headerBottom) return true;
      }
      return false;
    }

    function clearScrolling() {
      if (document.body.classList.contains(SCROLLING_CLASS)) {
        document.body.classList.remove(SCROLLING_CLASS);
      }
      for (var k = 0; k < headerEls.length; k++) {
        headerEls[k].classList.remove(SCROLLING_CLASS);
      }
    }

    function update() {
      heroActive = isHeroActive();
      if (heroActive) clearScrolling();
    }

    // Synchronous scroll listener: no rAF. Runs in the same tick as
    // Bricks's handler; ordering doesn't matter because the
    // MutationObserver below catches any add we miss.
    window.addEventListener('scroll', update, { passive: true });
    window.addEventListener('resize', update);

    // Backup: if anything (Bricks's rAF, a transitionend handler, etc.)
    // adds .scrolling while heroActive, strip it immediately as a
    // microtask, before the next paint.
    var classObserver = new MutationObserver(function () {
      if (heroActive) clearScrolling();
    });
    classObserver.observe(document.body, {
      attributes: true,
      attributeFilter: ['class']
    });
    for (var m = 0; m < headerEls.length; m++) {
      classObserver.observe(headerEls[m], {
        attributes: true,
        attributeFilter: ['class']
      });
    }

    update(); // Handle reload-mid-page
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();
L'Original

Ô long Sữa nguyên vị

Mang hương hoa trắng, vị tươi mát 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₫

Sáng sớm tĩnh lặng trên cao nguyên Lâm Đồng. Những làn sương mỏng vờn qua đồi chè đón nắng, nơi chồi trà non được hái tay một cách nâng niu.

Được chiết xuất bằng French press và quyện cùng kem sữa, Ô Long Sữa Nguyên Vị gợi mở cảm giác thanh sạch — hương hoa trắng thoảng nhẹ, khí mát của cao nguyên, và vị béo dịu đầy tinh tế.

Nguyên bản, hài hòa, sang trọng một cách nhẹ nhàng — như một nốt hương thanh thuần của buổi sớm đầu ngày.