/**
 * 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();
  }
})();

Trà hoa quả

(function() {
  // Smallest → largest
  const STEP_ORDER = [
    'step--3',
    'step--2',
    'step--1',
    'step-0',
    'step-1',
    'step-2',
    'step-3',
    'step-4',
    'step-5',
    'step-6',
    'step-7',
    'step-8',
    'step-9',
    'step-10'
  ];

  const el = document.getElementById('menu-title');
  if (!el) return;

  const MAX_STEP_NAME = el.dataset.step || 'step-9';
  const MIN_STEP_NAME = el.dataset.minStep || 'step-3';

  let maxIndex = STEP_ORDER.indexOf(MAX_STEP_NAME);
  let minIndex = STEP_ORDER.indexOf(MIN_STEP_NAME);

  if (maxIndex === -1) maxIndex = STEP_ORDER.length - 1;
  if (minIndex === -1) minIndex = 0;

  const MARGIN = 2; // px breathing room
  let currentIndex = maxIndex;

  let lastViewportWidth = 0;
  let lastDPR = window.devicePixelRatio || 1;
  let hasInitialAdjusted = false;

  // Hidden probe for accurate width measurement
  let probe = null;

  function ensureProbe() {
    if (probe) return probe;

    probe = document.createElement('span');
    probe.style.position = 'absolute';
    probe.style.left = '-9999px';
    probe.style.top = '0';
    probe.style.visibility = 'hidden';
    probe.style.whiteSpace = 'nowrap';
    probe.style.pointerEvents = 'none';
    document.body.appendChild(probe);

    const cs = getComputedStyle(el);
    probe.style.fontFamily = cs.fontFamily;
    probe.style.fontWeight = cs.fontWeight;
    probe.style.letterSpacing = cs.letterSpacing;
    probe.style.textTransform = cs.textTransform;
    probe.style.fontStyle = cs.fontStyle;
    probe.style.padding = cs.padding;
    probe.style.border = 'none';
    probe.style.margin = '0';

    probe.textContent = el.textContent;

    return probe;
  }

  // IMPORTANT: layout viewport only (no visualViewport)
  function getViewportWidth() {
    return (
      document.documentElement.clientWidth ||
      window.innerWidth ||
      el.getBoundingClientRect().width
    );
  }

  function measureWidthForStep(stepName) {
    const p = ensureProbe();
    p.style.fontSize = `var(--${stepName})`;
    return p.getBoundingClientRect().width;
  }

  function adjustForViewport(viewportWidth) {
    if (!el.offsetParent) return; // hidden element

    const maxAllowed = viewportWidth - 2 * MARGIN;
    let targetIndex = minIndex; // fallback

    // Find largest step that fits
    for (let idx = maxIndex; idx >= minIndex; idx--) {
      const stepName = STEP_ORDER[idx];
      const w = measureWidthForStep(stepName);
      if (w <= maxAllowed) {
        targetIndex = idx;
        break;
      }
    }

    if (hasInitialAdjusted && targetIndex === currentIndex) return;

    currentIndex = targetIndex;
    const stepName = STEP_ORDER[targetIndex];

    el.style.fontSize = `var(--${stepName})`;
    el.dataset.currentStep = stepName;
  }

  function loop() {
    const vw = getViewportWidth();
    const dpr = window.devicePixelRatio || 1;

    const widthChanged = Math.abs(vw - lastViewportWidth) > 0.5;
    const dprChanged = Math.abs(dpr - lastDPR) > 0.01;

    // First run: size immediately, no animation
    if (!hasInitialAdjusted) {
      lastViewportWidth = vw;
      lastDPR = dpr;
      adjustForViewport(vw);
      hasInitialAdjusted = true;

      // Enable smooth animation AFTER initial sizing
      setTimeout(function() {
        el.classList.add('tt-font-animate');
      }, 0);
    } else if (widthChanged || dprChanged) {
      lastViewportWidth = vw;
      lastDPR = dpr;
      adjustForViewport(vw);
    }

    requestAnimationFrame(loop);
  }

  function start() {
    lastViewportWidth = getViewportWidth();
    lastDPR = window.devicePixelRatio || 1;
    loop();
  }

  if (document.fonts && document.fonts.ready) {
    document.fonts.ready.then(start);
  } else {
    window.addEventListener('load', start);
  }
})();
(function() {
  const el = document.getElementById('menu-title');
  if (!el) return;

  // ----- 1) Split heading text into first word + rest -----
  const raw = (el.textContent || '').trim();
  const words = raw.split(/\s+/);

  // Only do this for titles with 3+ words, as you requested
  if (words.length < 3) return;

  const firstWordRaw = words.shift();              // first word
  const restText    = words.join(' ');             // remaining words

  // Capitalize first letter, rest lowercase (preserve accents)
  const firstWord =
    firstWordRaw.charAt(0).toUpperCase() +
    firstWordRaw.slice(1).toLowerCase();

  // Replace content with two spans
  el.innerHTML =
    '<span class="tt-title-first">' + firstWord + '</span>' +
    '<span class="tt-title-rest">'  + restText  + '</span>';

  const firstSpan = el.querySelector('.tt-title-first');
  if (!firstSpan) return;

  // ----- 2) Step scale (same ordering as other scripts) -----
  const STEP_ORDER = [
    'step--3',
    'step--2',
    'step--1',
    'step-0',
    'step-1',
    'step-2',
    'step-3',
    'step-4',
    'step-5',
    'step-6',
    'step-7',
    'step-8',
    'step-9',
    'step-10'
  ];

  function applyFirstWordSize(fromStepName) {
    if (!fromStepName) return;
    const idx = STEP_ORDER.indexOf(fromStepName);
    if (idx === -1) return;

    // 2 steps smaller, but never below smallest
    const smallerIdx = Math.max(idx - 2, 0);
    const smallerStep = STEP_ORDER[smallerIdx];

    firstSpan.style.fontSize = 'var(--' + smallerStep + ')';
  }

  // Initial sizing: use currentStep if present, else data-step
  const initialStep =
    el.dataset.currentStep || el.dataset.step || 'step-9';
  applyFirstWordSize(initialStep);

  // ----- 3) React whenever your sizing script changes the step -----
  const observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(m) {
      if (m.type === 'attributes' && m.attributeName === 'data-current-step') {
        applyFirstWordSize(el.dataset.currentStep);
      }
    });
  });

  observer.observe(el, { attributes: true });

})();
Trà được ủ lạnh nhiều giờ để giữ vị trà rõ ràng, mượt và tươi.