/* Việt Tín Hotel — Aman-inspired minimal luxury */
/* Note: switched the display serif from Cormorant Garamond to Playfair Display —
   Cormorant Garamond mis-renders Vietnamese double diacritics (ế, ộ, ặ, etc.),
   showing the tone mark as a floating separate glyph instead of stacked correctly.
   Playfair Display keeps a similarly elegant high-contrast look and renders
   Vietnamese correctly. */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Montserrat:wght@300;400;500;700&display=swap');

:root {
  --font-serif: 'Playfair Display', Georgia, serif;
  --font-sans: 'Montserrat', system-ui, sans-serif;
  --header-h: 72px;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Light (default) — white + navy #213059 + warm gold #b8956b.
   The bare :root selector here means this also applies as the fallback any
   time data-theme isn't (yet) set to "dark" — e.g. the instant before
   theme-init.js runs, or if it fails to load. IMPORTANT: :root matches the
   <html> element UNCONDITIONALLY, regardless of its data-theme attribute —
   it does NOT mean "only when no theme is set". That's exactly why this
   block must come BEFORE [data-theme="dark"] below: with equal-specificity
   selectors, the later rule in source order wins a tie, so if this block
   were placed after [data-theme="dark"] (as it briefly was — a bug caught
   after a live phone/desktop test showed dark mode stuck rendering light
   colors with the toggle icon still saying "dark"), its declarations would
   override the dark theme's on every element, even ones explicitly marked
   data-theme="dark", and dark mode could never render at all. Same pattern
   the original dark-default version of this file used, just with the two
   themes' roles swapped: :root bundled with the CURRENT default theme,
   placed first; the other theme's override placed second so it can win. */
:root,
[data-theme="light"] {
  --color-bg: #ffffff;
  --color-bg-soft: #f4f1eb;
  --color-text: #15243d;
  --color-text-muted: #1f3352;
  --color-accent: #15243d;
  --color-accent-warm: #6b542f;
  --color-accent-hover: #2d4278;
  --color-price: #8a5a1f;
  --color-on-accent: #ffffff;
  --color-border: rgba(33, 48, 89, 0.14);
  --color-header-fade: rgba(255, 255, 255, 0.92);
  --color-header-scrolled: rgba(255, 255, 255, 0.97);
  /* No overlay on hero banners in light mode, per explicit request — was the
     navy gradient below (kept commented for reference/rollback):
     linear-gradient(to top, rgba(33, 48, 89, 0.72) 0%, rgba(33, 48, 89, 0.2) 50%, rgba(33, 48, 89, 0.35) 100%) */
  --color-hero-overlay: none;
  --color-card-overlay: linear-gradient(to top, rgba(33, 48, 89, 0.88), transparent 60%);
  --color-on-hero: #ffffff;
  --color-on-hero-muted: rgba(255, 255, 255, 0.9);
  /* Hero banners no longer have a colored scrim in light mode (see
     --color-hero-overlay above), but text stays white per explicit request
     rather than switching to dark — relies on the hero photos themselves
     being dark/moody enough for white text to read without a scrim. Kept as
     its own variable (not shared with --color-on-hero) so it's easy to
     retune independently of .card__overlay, which keeps its own dark scrim
     and is unaffected either way. */
  --color-hero-text: #ffffff;
  --color-hero-text-muted: rgba(255, 255, 255, 0.9);
}

/* Dark — must stay AFTER the light/:root block above so it wins the
   equal-specificity tie on elements explicitly marked data-theme="dark".
   See the long comment on that block for exactly why. */
[data-theme="dark"] {
  --color-bg: #0f0f0f;
  --color-bg-soft: #1a1a1a;
  --color-text: #f5f2ed;
  --color-text-muted: #a8a29e;
  --color-accent: #8b7355;
  --color-accent-warm: #8b7355;
  --color-accent-hover: #9d8468;
  --color-price: #d9a85c;
  --color-on-accent: #f5f2ed;
  --color-border: rgba(245, 242, 237, 0.12);
  --color-header-fade: rgba(15, 15, 15, 0.9);
  --color-header-scrolled: rgba(15, 15, 15, 0.95);
  /* Lightened slightly from an earlier, darker version (0.85/0.2/0.4) per
     explicit request — hero photos read as too dim in dark mode. Kept the
     same shape (darkest at the bottom, where .hero's flex-end-aligned
     title/subtitle text sits, needing the most contrast; lightest in the
     middle, where the photo itself should show through clearest). */
  --color-hero-overlay: linear-gradient(to top, rgba(15, 15, 15, 0.68) 0%, rgba(15, 15, 15, 0.12) 50%, rgba(15, 15, 15, 0.28) 100%);
  --color-card-overlay: linear-gradient(to top, rgba(15, 15, 15, 0.9), transparent 60%);
  --color-on-hero: #f5f2ed;
  --color-on-hero-muted: #a8a29e;
  /* Hero banner title/subtitle text only (kept separate from --color-on-hero,
     which .card__overlay also uses over its own always-on dark scrim) — dark
     theme keeps the same white-on-dark-overlay look as everywhere else. */
  --color-hero-text: #f5f2ed;
  --color-hero-text-muted: #a8a29e;
}

/* Light: darker, slightly heavier type on white & sand */
[data-theme="light"] body {
  font-weight: 400;
  color: var(--color-text);
}

[data-theme="light"] .nav-desktop,
[data-theme="light"] .section-label,
[data-theme="light"] .footer-info,
[data-theme="light"] .footer-copy,
[data-theme="light"] .form-group label,
[data-theme="light"] .form-note,
[data-theme="light"] .amenities li,
[data-theme="light"] .section p,
[data-theme="light"] .split__text p,
[data-theme="light"] .split__text .price,
[data-theme="light"] .room-meta,
[data-theme="light"] .price-table__sub,
[data-theme="light"] .policy-group li,
[data-theme="light"] .room-policies__note,
[data-theme="light"] .footer-col a,
[data-theme="light"] .faq-item p,
[data-theme="light"] .text-accent-warm {
  color: var(--color-text-muted);
  font-weight: 500;
}

[data-theme="light"] .nav-desktop a.active {
  color: var(--color-text);
  font-weight: 500;
}

[data-theme="light"] .section-label,
[data-theme="light"] .split__text .price,
[data-theme="light"] .text-accent-warm {
  color: var(--color-accent-warm);
}

[data-theme="light"] .logo,
[data-theme="light"] .section h2,
[data-theme="light"] .split__text h3 {
  color: var(--color-text);
}

[data-theme="light"] .card__overlay h3 {
  color: var(--color-on-hero);
  font-weight: 400;
}

[data-theme="light"] .card__overlay p,
[data-theme="light"] .card__overlay .price {
  color: var(--color-on-hero-muted);
  font-weight: 400;
}

[data-theme="light"] a:hover {
  opacity: 1;
}

[data-theme="light"] .btn-book {
  color: var(--color-text);
  border-color: var(--color-text);
  background: transparent;
  font-weight: 500;
}

[data-theme="light"] .btn-book:hover {
  background: var(--color-text);
  color: var(--color-bg);
  opacity: 1;
}

[data-theme="light"] .btn:not(.btn--primary) {
  color: var(--color-text);
  border-color: var(--color-border);
  font-weight: 500;
}

[data-theme="light"] .btn:not(.btn--primary):hover {
  color: var(--color-text);
  border-color: var(--color-accent);
}

[data-theme="light"] .btn--primary,
[data-theme="light"] .btn--primary:hover {
  color: var(--color-on-accent);
  background: var(--color-accent);
  border-color: var(--color-accent);
  font-weight: 500;
}

[data-theme="light"] .btn--primary:hover {
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}

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

html { scroll-behavior: smooth; }

body {
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 15px;
  line-height: 1.7;
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  /* Playfair Display (--font-serif) defaults to old-style figures — digits
     with varying height/baseline (e.g. "3"/"7" dip below the baseline, "6"
     rises above x-height) instead of sitting evenly like the rest of the
     text. Fine for a page number in a novel, but it reads as broken/uneven
     wherever the serif font wraps real data — phone numbers, prices. Set
     once here and inherited everywhere (including every --font-serif
     element) rather than patched per-selector; a global reset like this is
     safe because it's a no-op on --font-sans (Montserrat), which already
     renders lining figures by default. */
  font-variant-numeric: lining-nums;
}

/* Avoid orphaned single words on the last line of any heading */
h1, h2, h3, h4 { text-wrap: balance; }

img { max-width: 100%; height: auto; display: block; }

a { color: inherit; text-decoration: none; transition: opacity 0.3s var(--ease); }
a:hover { opacity: 0.7; }

/* Header */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 clamp(1.5rem, 4vw, 3rem);
  background: linear-gradient(to bottom, var(--color-header-fade), transparent);
  transition: background 0.4s var(--ease);
}

.site-header.scrolled {
  background: var(--color-header-scrolled);
  backdrop-filter: blur(8px);
}

[data-theme="light"] .site-header {
  background: #ffffff;
  border-bottom: 1px solid var(--color-border);
}

[data-theme="light"] .site-header.scrolled {
  background: #ffffff;
  backdrop-filter: none;
  box-shadow: 0 1px 0 var(--color-border);
}

.logo {
  font-family: var(--font-serif);
  font-size: 1.5rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Image logo (uploaded via Cài đặt chung → Logo). Height is capped relative
   to --header-h so it always fits the fixed-height header without growing
   the header itself; width follows naturally so non-square logos aren't
   distorted. */
.logo--image {
  display: inline-flex;
  align-items: center;
}

.logo--image img {
  display: block;
  height: calc(var(--header-h) * 0.8);
  width: auto;
  max-width: 260px;
  object-fit: contain;
}

.nav-desktop {
  display: none;
  gap: 2rem;
  font-size: 0.7rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.nav-desktop a.active { opacity: 1; border-bottom: 1px solid var(--color-accent); padding-bottom: 2px; }

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.header-dropdown {
  position: relative;
}

.header-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: none;
  background: none;
  color: var(--color-text);
  cursor: pointer;
  border-radius: 50%;
  transition: background 0.3s var(--ease), opacity 0.3s var(--ease);
}

.header-icon-btn:hover {
  opacity: 1;
  background: var(--color-border);
}

.header-icon {
  width: 1.15rem;
  height: 1.15rem;
}

.header-popup {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  z-index: 150;
  min-width: 8.5rem;
  padding: 0.35rem 0;
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease), visibility 0.25s;
}

[data-theme="light"] .header-popup {
  background: #ffffff;
  box-shadow: 0 8px 24px rgba(21, 36, 61, 0.12);
}

.header-dropdown.is-open .header-popup {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.header-popup__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.header-popup__item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.55rem 1rem;
  font-family: inherit;
  font-size: 0.65rem;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--color-text-muted);
  background: none;
  border: none;
  cursor: pointer;
  transition: background 0.2s var(--ease), color 0.2s var(--ease);
}

.header-popup__item .header-icon {
  width: 1rem;
  height: 1rem;
  flex-shrink: 0;
}

.header-popup__item:hover {
  opacity: 1;
  background: var(--color-border);
  color: var(--color-text);
}

.header-popup__item.is-active {
  color: var(--color-text);
  background: var(--color-border);
}

.header-popup__item.is-soon {
  cursor: default;
  opacity: 0.45;
}

.header-popup__item.is-soon:hover {
  background: none;
  color: var(--color-text-muted);
}

.header-popup__flag {
  font-size: 1rem;
  line-height: 1;
  flex-shrink: 0;
}

.header-popup__abbr {
  flex: 1;
  text-align: left;
}

[data-theme="light"] .header-icon-btn {
  color: var(--color-text);
}

[data-theme="light"] .header-popup__item {
  color: var(--color-text-muted);
  font-weight: 500;
}

[data-theme="light"] .header-popup__item.is-active,
[data-theme="light"] .header-popup__item:hover {
  color: var(--color-text);
}

.btn-book {
  margin-left: 0.5rem;
  font-size: 0.65rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.65rem 1.25rem;
  border: 1px solid var(--color-text);
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
}

.btn-book:hover {
  opacity: 1;
  background: var(--color-text);
  color: var(--color-bg);
}

.menu-toggle {
  display: flex;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
}

.menu-toggle span {
  width: 22px;
  height: 1px;
  background: var(--color-text);
}

.nav-mobile {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: var(--color-bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s var(--ease), visibility 0.4s;
}

.nav-mobile.open { opacity: 1; visibility: visible; }

.nav-mobile a {
  font-family: var(--font-serif);
  font-size: 2rem;
  letter-spacing: 0.1em;
}

/* Compact header on small screens — logo + language/theme icons + book button +
   hamburger all compete for one row, so scale everything down to fit on one line
   instead of letting the logo wrap and collide with the hero below it. */
@media (max-width: 640px) {
  .site-header { padding: 0 1.1rem; }

  .logo {
    font-size: clamp(0.7rem, 3.6vw, 0.95rem);
    letter-spacing: 0.08em;
  }

  .logo--image img {
    height: calc(var(--header-h) * 0.62);
    max-width: 170px;
  }

  .header-actions { gap: 0.15rem; }

  .header-icon-btn { width: 1.9rem; height: 1.9rem; }
  .header-icon { width: 1rem; height: 1rem; }

  .btn-book {
    margin-left: 0.15rem;
    padding: 0.5rem 0.7rem;
    font-size: 0.55rem;
    letter-spacing: 0.1em;
  }

  .menu-toggle { padding: 4px 2px; }
}

/* Hero */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: clamp(4rem, 12vh, 8rem);
  overflow: hidden;
}

/* Shared placeholder look for any photo area (hero, split, card) before a real
   image loads — a soft branded gradient instead of a flat void that reads as
   a broken image. Once JS successfully loads a photo it sets background-image
   inline, which paints over this. */
.hero-bg,
.split__image,
.card__img,
.experience-card__img,
.experience-modal__img,
.testimonial-slide__img,
.pillar-card__img,
.gallery-strip__item,
.about-host__avatar,
.contact-layout__image,
.booking-room-summary__img {
  background-color: var(--color-bg-soft);
  background-image:
    radial-gradient(120% 120% at 50% 15%, color-mix(in srgb, var(--color-accent) 16%, transparent), transparent 60%),
    linear-gradient(160deg, var(--color-bg-soft), var(--color-bg));
}

.img-pending { animation: imgPulse 1.8s ease-in-out infinite; }

@keyframes imgPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.75; }
}

.hero-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transform: scale(1.02);
  animation: heroZoom 20s ease-out forwards;
}

@keyframes heroZoom {
  to { transform: scale(1); }
}

.hero-overlay {
  position: absolute;
  inset: 0;
  background: var(--color-hero-overlay);
}

.hero-content {
  position: relative;
  z-index: 1;
  text-align: center;
  padding: 0 1.5rem;
  max-width: 900px;
  color: var(--color-hero-text);
}

/* Light mode only: no overlay sits over the photo (see .hero-overlay above,
   --color-hero-overlay: none) and the photos themselves can be bright/vivid,
   so white text needs its own contrast instead of relying on the image
   being dark underneath it. A soft shadow directly on the letters — not a
   layer over the photo — keeps every hero image's true color fully visible
   while still guaranteeing the text reads. Dark theme keeps its overlay and
   doesn't need this. */
[data-theme="light"] .hero-content {
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.45), 0 8px 30px rgba(0, 0, 0, 0.3);
}

/* The subtitle line is small and thin (uppercase, wide letter-spacing) —
   noticeably weaker than the bold serif h1 under the same shadow, so it
   gets a slightly stronger one rather than inheriting the h1's. */
[data-theme="light"] .hero-content p {
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 2px 6px rgba(0, 0, 0, 0.5), 0 4px 16px rgba(0, 0, 0, 0.35);
}

.hero-content h1 {
  font-family: var(--font-serif);
  font-size: clamp(2.5rem, 8vw, 5rem);
  font-weight: 400;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  margin-bottom: 0.75rem;
  text-wrap: balance;
}

/* Optional image/wordmark swapped in for the h1 text above (CMS: "Cài đặt
   chung" → "Ảnh thay cho dòng chữ..."). Sized to roughly fill the same
   footprint the big serif h1 would take (clamp mirrors its font-size range)
   so the hero's overall balance doesn't shift depending on which is shown.
   Hidden via [hidden] when no image is set — see renderHome() in main.js. */
.hero-title-image {
  display: block;
  width: auto;
  height: clamp(3.5rem, 9vw, 6.5rem);
  max-width: 90%;
  margin: 0 auto 0.75rem;
  object-fit: contain;
}

.hero-title-image[hidden] { display: none; }

/* Short hero (inner pages) titles are often full phrases ("Yêu cầu đặt phòng",
   "Câu chuyện của chúng tôi") rather than the single short word the homepage's
   huge display size assumes — scale it down so longer Vietnamese titles don't
   wrap into an unbalanced two-liner. */
.hero--short .hero-content h1 {
  font-size: clamp(1.75rem, 5.5vw, 3rem);
  letter-spacing: 0.1em;
}

.hero-content p {
  font-size: 0.75rem;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: var(--color-hero-text-muted);
}

/* Small overline above the hero title (About page) — same treatment as the
   tagline below the title, just placed first so it reads as "Our story /
   ABOUT US / where every moment is cared for" top to bottom. */
.hero-content__label {
  display: block;
  font-size: 0.7rem;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: var(--color-hero-text-muted);
  margin-bottom: 1rem;
}

[data-theme="light"] .hero-content__label { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 2px 6px rgba(0, 0, 0, 0.5), 0 4px 16px rgba(0, 0, 0, 0.35); }

/* Dark mode was relying entirely on .hero-overlay's dark scrim for contrast
   (no shadow at all), which was fine until the overlay was lightened above
   (per explicit request — photos were reading too dim). The bold white h1
   still holds up on its own, but the subtitle/label lines use the dimmer
   --color-hero-text-muted at a small size, thin uppercase letters — exactly
   the combination that gets hard to read once the scrim is lighter. Same
   fix as light mode above, just a lighter shadow since dark mode still has
   the overlay doing part of the work. */
[data-theme="dark"] .hero-content p,
[data-theme="dark"] .hero-content__label {
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.4);
}

.hero--short { min-height: 55vh; }

/* No mobile-specific override here on purpose. Several fixed-ratio/contain
   treatments were tried on this block below 640px (a 3:4 band, a 3:2 band,
   a per-photo dynamic ratio via JS) to avoid cropping the photo on narrow
   screens — each fixed one problem but introduced another (empty letterbox,
   title overlapping the header, title overflowing off-screen). Per explicit
   request, mobile now falls back to exactly the same rule as desktop/tablet
   above: min-height: 100vh (55vh for --short) + background-size: cover on
   .hero-bg — the photo crops to fill the viewport-relative box, same as it
   always did before any mobile-only styling was added. */

/* Sections */
.section {
  padding: clamp(4rem, 10vw, 7rem) clamp(1.5rem, 5vw, 4rem);
}

.section--narrow { max-width: 720px; margin: 0 auto; text-align: center; }

/* Optional small mark above the homepage intro's label/heading (CMS: "Cài
   đặt chung" → "Ảnh/logo phía trên..."). Hidden entirely via [hidden] when
   no image is set (see renderHomeIntroLogo() in main.js) rather than
   collapsing to 0 height, so there's no leftover empty gap either way. */
.intro-logo {
  display: block;
  max-height: 9rem;
  width: auto;
  max-width: 400px;
  margin: 0 auto 1.5rem;
  object-fit: contain;
}

.intro-logo[hidden] { display: none; }

.section-label {
  font-size: 0.65rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
  margin-bottom: 1rem;
}

.section h2 {
  font-family: var(--font-serif);
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 400;
  margin-bottom: 1.5rem;
  line-height: 1.2;
}

.section p { color: var(--color-text-muted); max-width: 560px; margin: 0 auto 2rem; }

.section--compact {
  padding-top: 0;
  padding-bottom: clamp(3rem, 6vw, 5rem);
}

.section--flush {
  padding-top: 0;
  padding-bottom: 0;
}

/* Home — seasonal promo banner (clickable image linking to Booking).
   CMS-editable from the "Trang chủ" tab's "Mục khuyến mãi" card (photo,
   optional mobile-portrait variant, and display position — not through
   IMAGE_MANIFEST/"Quản lý ảnh" like most images, see the comment above
   GET /home in adminContent.ts) so the admin can swap/remove/reposition the
   campaign graphic without a code deploy. Hidden via [hidden] on the whole
   <section> (not just the <img>) when no image is set — see renderHome() in
   main.js — so an inactive promotion leaves no empty gap. .section--compact
   zeroes the top padding so it reads as an extension of whichever section it
   currently sits right after (position is admin-controlled and can move via
   applyPromoPosition() in main.js — this padding still reads fine at any of
   its anchor points, since every one of them is itself a normal
   .section/.section--flush boundary). */
.promo-section { padding-top: clamp(1.5rem, 4vw, 2.5rem); }

.promo-section[hidden] { display: none; }

.promo-banner {
  display: block;
  max-width: 1100px;
  margin: 0 auto;
  overflow: hidden;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.promo-banner img { width: 100%; height: auto; }

@media (hover: hover) {
  .promo-banner:hover {
    transform: translateY(-2px);
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.16);
  }
}

/* Home — highlights */
.highlights {
  display: grid;
  gap: 2rem;
  max-width: 1100px;
  margin: 0 auto;
}

@media (min-width: 640px) {
  .highlights { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .highlights { grid-template-columns: repeat(4, 1fr); gap: 2.5rem; }
}

.highlight {
  text-align: center;
  padding-top: 1.5rem;
  border-top: 1px solid var(--color-border);
}

.highlight__title {
  font-family: var(--font-serif);
  font-size: 1.25rem;
  font-weight: 400;
  margin-bottom: 0.75rem;
  color: var(--color-text);
}

.highlight__text {
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--color-text-muted);
  margin: 0 auto;
  max-width: 16rem;
}

/* Home — section title row. Used above the Rooms and Experiences teasers
   only — deliberately full-width (no max-width/auto-margin centering) so
   its left edge lines up exactly with the photo grid/strip below it rather
   than sitting inset from it (per explicit request; the old 1200px cap
   centered this row inside the section's padded width, which is wider than
   1200px on most desktop viewports, creating a visible left-edge gap
   against the images). The heading itself is also sized down from the
   generic .section h2 (per explicit request: "đang để quá to"). */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem 2rem;
  margin: 0 0 2.5rem;
  text-align: left;
}

.section-head__text h2 {
  font-size: clamp(1.75rem, 3vw, 2.25rem);
  margin-bottom: 0;
}

.section-head__link {
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
  opacity: 1;
  flex-shrink: 0;
}

.section-head__link:hover { opacity: 0.75; }

.card--link {
  display: block;
  color: inherit;
}

.card--link:hover { opacity: 1; }

/* Home — CTA band */
.cta-band {
  background: var(--color-bg-soft);
  padding: clamp(4rem, 10vw, 6rem) clamp(1.5rem, 5vw, 4rem);
  text-align: center;
}

.cta-band__inner { max-width: 560px; margin: 0 auto; }

.cta-band h2 {
  font-family: var(--font-serif);
  font-size: clamp(2rem, 4vw, 2.75rem);
  font-weight: 400;
  margin-bottom: 1rem;
  color: var(--color-text);
}

.cta-band p {
  margin: 0 auto 2rem;
  max-width: 480px;
}

[data-theme="light"] .highlight__title,
[data-theme="light"] .section-head__text h2,
[data-theme="light"] .cta-band h2 {
  color: var(--color-text);
}

[data-theme="light"] .highlight__text,
[data-theme="light"] .cta-band p {
  color: var(--color-text-muted);
  font-weight: 500;
}

/* Split block */
.split {
  display: grid;
  gap: 0;
}

@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; }

  /* Homepage's own location block only (.home-location) — not
     About's (.about-location) or Contact's (.contact-location), which
     keep the default even split — image column wider than the text
     column at desktop width, per explicit request (6:4 instead of 5:5). */
  .home-location { grid-template-columns: 6fr 4fr; }
}

.split__image {
  min-height: 400px;
  background-size: cover;
  background-position: center;
}

.split__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(2rem, 6vw, 5rem);
  background: var(--color-bg-soft);
}

.split__text h3 {
  font-family: var(--font-serif);
  font-size: clamp(1.5rem, 1.1rem + 1.8vw, 2rem);
  margin-bottom: 1rem;
}

.split__text p { margin: 0 0 1rem; text-align: left; }

/* Cards grid — generous gutters between photos (rather than the old
   hairline 1px separator) so the grid reads as airy/uncrowded, closer to
   how a site like Aman lets each room/experience photo breathe. */
.grid-cards {
  display: grid;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}

@media (min-width: 768px) {
  /* 2 columns first so cards stay wide enough for titles like "Executive Suite"
     to fit on one line; bumped to 3 only once there's enough room per card. */
  .grid-cards { grid-template-columns: repeat(2, 1fr); }
  .grid-cards--2 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .grid-cards { grid-template-columns: repeat(3, 1fr); }
  /* Re-assert after the rule above: .grid-cards--2 must always stay 2 columns,
     regardless of viewport, since it marks grids with exactly 2 items. */
  .grid-cards--2 { grid-template-columns: repeat(2, 1fr); }
}

/* Homepage — Trải nghiệm teaser strip. Shows every experience (not just a
   handful) as a horizontally scrollable row instead of a fixed grid, so the
   count can grow without needing a taller and taller homepage. Scrolls
   natively via touch/trackpad; initDragStrip() in main.js adds mouse-drag
   support. No edge fades or ‹ › buttons here (removed per explicit
   request) — just the strip itself, drag/scroll only.

   Deliberately NO scroll-behavior:smooth and NO scroll-snap-type on the
   strip itself (per explicit "vuốt không mượt" report): setting scrollLeft
   directly every pointermove frame (mouse-drag) while scroll-behavior is
   smooth makes the browser re-animate/ease toward a new target on every
   single frame, which fights the drag and feels laggy/rubbery instead of
   1:1 with the pointer; scroll-snap-type also has no is-dragging exception
   for touch (only mouse gets that class), so it was fighting touch swipes
   too.

   .is-dragging (added in main.js for the duration of a mouse-drag) also
   turns off text/image selection and the tiles' hover-lift transform below
   — with several tiles' worth of hover targets sliding under the pointer
   during a fast drag, that transform kept re-triggering per tile and read
   as stutter even once the scroll position itself was moving smoothly
   (see initDragStrip()'s rAF-throttled scrollLeft writes in main.js for
   the other half of that fix). */
.exp-strip-wrap { position: relative; }

.exp-strip {
  --exp-gap: clamp(1.25rem, 2.5vw, 2rem);
  display: flex;
  gap: var(--exp-gap);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
}

.exp-strip::-webkit-scrollbar { display: none; }

.exp-strip.is-dragging {
  cursor: grabbing;
  user-select: none;
  -webkit-user-select: none;
}

.exp-strip.is-dragging .exp-tile {
  pointer-events: none;
}

/* Aman-style tile: photo on top, caption below with NO horizontal padding —
   the caption text's left/right edges line up exactly with the photo's
   edges (per explicit request, referencing aman.com's own listing tiles,
   where the caption text block is flush with its photo rather than inset).
   This intentionally differs from .experience-card on the dedicated
   Experiences page (which insets its caption in a padded card body) — this
   strip is a plainer, closer match to the Aman reference.

   Sizing shows exactly 3 tiles per view on desktop (per explicit request),
   scaling up notably from the old fixed 340px cap; 2 on tablet, ~1 (with a
   peek of the next) on mobile to hint the strip is draggable — same
   768px/1024px breakpoints as .grid-cards elsewhere on the site. */
.exp-tile {
  display: block;
  flex: 0 0 82%;
  color: inherit;
  transition: transform 0.3s var(--ease);
}

@media (min-width: 768px) {
  .exp-tile { flex: 0 0 calc((100% - var(--exp-gap)) / 2); }
}

@media (min-width: 1024px) {
  .exp-tile { flex: 0 0 calc((100% - 2 * var(--exp-gap)) / 3); }
}

.exp-tile:hover,
.exp-tile:focus-visible {
  transform: translateY(-6px);
}

.exp-tile__img {
  aspect-ratio: 1 / 1;
  background-size: cover;
  background-position: center;
}

.exp-tile__body {
  padding-top: 1.1rem;
}

.exp-tile__body h3 {
  font-family: var(--font-serif);
  font-size: 1.15rem;
  font-weight: 400;
  margin-bottom: 0.5rem;
  color: var(--color-text);
}

.exp-tile__body p {
  font-size: 0.85rem;
  line-height: 1.7;
  color: var(--color-text-muted);
  margin: 0;
}

/* Experiences page — Aman-style tile grid: photo on top, caption below in a
   soft neutral card body, instead of the overlay-on-photo .card pattern
   used for room/dining teasers. Referenced from aman.com's Amanoi
   "Accommodation" listing page (image + heading + short text, generous
   whitespace, calm neutral tone) per explicit request. */
.experience-grid {
  display: grid;
  gap: clamp(2rem, 4vw, 3rem) clamp(1.5rem, 3vw, 2rem);
  max-width: 1320px;
  margin: 0 auto;
}

@media (min-width: 640px) {
  .experience-grid { grid-template-columns: repeat(2, 1fr); }
}

/* 3 photos per row on desktop, matching the Amanoi reference. */
@media (min-width: 1024px) {
  .experience-grid { grid-template-columns: repeat(3, 1fr); }
}

/* Each tile is a real <button> (not just a styled div) so clicking anywhere
   on the card — image or caption — opens the detail popup, with native
   keyboard/focus support for free. Reset button chrome back to how the
   card looked as a plain element. */
.experience-card {
  display: block;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.experience-card:hover,
.experience-card:focus-visible {
  transform: translateY(-6px);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.16);
}

.experience-card:focus-visible {
  outline: 2px solid var(--color-accent-warm);
  outline-offset: 4px;
}

.experience-card__img {
  aspect-ratio: 4/3;
  background-size: cover;
  background-position: center;
}

.experience-card__body {
  background: var(--color-bg-soft);
  padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1.5rem, 3vw, 2rem);
}

.experience-card__body h3 {
  font-family: var(--font-serif);
  font-size: 1.4rem;
  font-weight: 400;
  margin-bottom: 0.75rem;
  color: var(--color-text);
}

.experience-card__body p {
  font-size: 0.88rem;
  line-height: 1.75;
  color: var(--color-text-muted);
  margin: 0;
}

/* Experience detail popup — clicking any tile opens this: the item's full
   photo set (with prev/next if it has more than one) up top, its name and
   full description below. Same overlay pattern as .lightbox/.detail-modal
   for visual consistency, but combines both (a photo stage AND a text
   body) since this popup's whole purpose is "enlarge the photo, show the
   write-up" together rather than either alone. */
body.experience-modal-open { overflow: hidden; }

.experience-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(10, 10, 10, 0.82);
  align-items: center;
  justify-content: center;
  padding: clamp(1rem, 4vw, 3rem);
}

.experience-modal.is-open { display: flex; }

.experience-modal__panel {
  position: relative;
  width: min(880px, 100%);
  max-height: 90vh;
  overflow-y: auto;
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
}

.experience-modal__stage {
  position: relative;
  aspect-ratio: 16/9;
  overflow: hidden;
}

/* Separate element from .experience-modal__stage itself so setBg()'s
   img-pending pulse (which animates this element's opacity while a photo
   loads) doesn't also fade the close/prev/next buttons layered on top. */
.experience-modal__img {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

@media (max-width: 640px) {
  .experience-modal__stage { aspect-ratio: 4/3; }
}

.experience-modal__close,
.experience-modal__nav {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 15, 15, 0.4);
  border: none;
  border-radius: 50%;
  color: #f5f2ec;
  cursor: pointer;
  line-height: 1;
  opacity: 0.85;
  transition: opacity 0.2s, background 0.2s;
}

.experience-modal__close:hover,
.experience-modal__nav:hover { opacity: 1; background: rgba(15, 15, 15, 0.6); }

.experience-modal__close {
  top: clamp(0.6rem, 2vw, 1.25rem);
  right: clamp(0.6rem, 2vw, 1.25rem);
  width: 2.25rem;
  height: 2.25rem;
  font-size: 1.4rem;
}

.experience-modal__nav {
  top: 50%;
  transform: translateY(-50%);
  width: 2.75rem;
  height: 2.75rem;
  font-size: 1.75rem;
}

.experience-modal__nav--prev { left: clamp(0.5rem, 2vw, 1.25rem); }
.experience-modal__nav--next { right: clamp(0.5rem, 2vw, 1.25rem); }

.experience-modal__count {
  position: absolute;
  bottom: clamp(0.6rem, 2vw, 1.25rem);
  left: 50%;
  transform: translateX(-50%);
  color: rgba(245, 242, 236, 0.85);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  /* Same reasoning as the hero title/subtitle text-shadow elsewhere in this
     file: photo content is unpredictable in brightness (a light/white
     photo would otherwise wash this near-white counter out), and a shadow
     keeps it legible without needing an opaque backdrop chip. */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.45);
}

.experience-modal__body {
  padding: clamp(1.5rem, 4vw, 2.5rem);
}

.experience-modal__body h3 {
  font-family: var(--font-serif);
  font-size: clamp(1.5rem, 3vw, 1.85rem);
  font-weight: 400;
  margin-bottom: 1rem;
  color: var(--color-text);
}

.experience-modal__body p {
  font-size: 0.92rem;
  line-height: 1.8;
  color: var(--color-text-muted);
  margin: 0;
}

.card {
  position: relative;
  background: var(--color-bg);
  overflow: hidden;
  aspect-ratio: 4/5;
  group: card;
}

.card__img {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transition: transform 0.8s var(--ease);
}

.card:hover .card__img { transform: scale(1.05); }

.card__overlay {
  position: absolute;
  inset: 0;
  background: var(--color-card-overlay);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 2rem;
  color: var(--color-on-hero);
}

.card h3 {
  font-family: var(--font-serif);
  font-size: 1.75rem;
  margin-bottom: 0.5rem;
}

.card__overlay p {
  font-size: 0.85rem;
  color: var(--color-on-hero-muted);
}

.card .price {
  font-size: 0.7rem;
  letter-spacing: 0.15em;
  color: var(--color-on-hero-muted);
  text-transform: uppercase;
}

.card__overlay .price { color: rgba(255, 255, 255, 0.9); }

/* CTA row */
.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
  margin-top: 2rem;
}

.btn {
  display: inline-block;
  font-size: 0.65rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 1rem 2rem;
  border: 1px solid var(--color-border);
  transition: border-color 0.3s, background 0.3s;
}

.btn:hover { opacity: 1; border-color: var(--color-accent); }

.btn--primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}

.btn--primary:hover {
  opacity: 1;
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}

.btn--small {
  padding: 0.6rem 1.1rem;
  font-size: 0.6rem;
}

/* Rooms page: each category is a "split" showcase (large image + name,
   evocative blurb, prominent from-price, booking CTA) — sells the room the
   way a customer decides, rather than opening straight into a data table.
   Full pricing/amenity detail opens in a popup (see .detail-modal below).
   Mobile: full-bleed, stacked, separated by a hairline border, price detail
   expands inline via the native <details>. */
.room-showcase {
  position: relative;
  border-bottom: 1px solid var(--color-border);
}

.room-showcase:last-child { border-bottom: none; }

/* Mobile: the first showcase sat flush against the hero's bottom edge (0px
   gap) — same issue as the desktop card layout below, fixed the same way:
   a bit of top breathing room so the hero and the first room don't blend
   into each other. */
@media (max-width: 899px) {
  #rooms-grid {
    padding-top: clamp(1.5rem, 6vw, 2.25rem);
  }
}

@media (min-width: 900px) {
  .room-showcase--reverse .split__image { order: 2; }

  /* Desktop only: bind each hạng phòng's photo + text into one
     self-contained boxed container (instead of bleeding full viewport
     width) with real gaps between tiers instead of a single hairline
     separator — reads as distinct "cards" rather than one long continuous
     slab. */
  #rooms-grid {
    /* Top padding was 0 — the first showcase box sat flush against the
       hero's bottom edge with zero breathing room, unlike every other gap
       on the page. Matches the bottom value so the grid reads as evenly
       framed rather than glued to the hero. */
    padding: clamp(3rem, 6vw, 4rem) clamp(1.5rem, 4vw, 3rem) clamp(3rem, 6vw, 4rem);
  }

  .room-showcase {
    max-width: 1200px;
    margin: 0 auto clamp(2rem, 4vw, 3rem);
    border: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-bg-soft);
  }

  .room-showcase:last-child {
    margin-bottom: 0;
  }

  /* Every showcase box is the SAME fixed height on desktop, regardless of
     which hạng phòng it is — a common standard rather than each box sizing
     itself to its own content. The image column fills it edge-to-edge
     (background-size:cover just shows a bit more/less of the same photo),
     and the text column's content is kept within that budget: the blurb is
     clamped to 2 lines (below) and the price/amenity detail no longer
     lives inline at all — it opens in .detail-modal instead — so nothing
     in the text column can push the box taller than this. */
  .split--room {
    /* Tall enough that even the longest tier title (Family Room's 2-line
       "· SÂN THỂ THAO (PICKLEBALL)" subtitle) plus its full content stack
       fits, with a slightly tighter padding budget than the base
       .split__text (below) to keep the box from being taller than it needs
       to be. Verified against all 3 current categories at every desktop
       width from 960–1920px. */
    height: 624px;
  }

  /* Shorter tiers (e.g. Deluxe) don't have enough content to fill 624px —
     rather than centering that leftover as one block (which pushes the
     "Xem bảng giá" trigger up into the middle of a lot of empty space
     above AND below it), keep the sell copy anchored to the top like
     normal reading order and let .room-detail's margin-top:auto (below)
     absorb the slack by pinning the trigger to the bottom edge instead —
     every box's trigger then lines up at the same spot regardless of tier. */
  .split--room .split__text {
    justify-content: flex-start;
    padding: clamp(2rem, 5vw, 4rem);
  }
}

.room-blurb {
  color: var(--color-text-muted);
}

@media (min-width: 900px) {
  /* Clamped to a fixed 2 lines on desktop so blurb length doesn't push the
     now-uniform-height showcase box (above) past its budget — a longer
     blurb just gets a "…" instead of growing the box. Mobile is untouched
     (unclamped, full text) since boxes there aren't height-constrained. */
  .split--room .room-blurb {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

.room-price-lead {
  font-family: var(--font-serif);
  font-size: clamp(1.55rem, 1.25rem + 1.3vw, 1.95rem);
  color: var(--color-text);
  margin: 0.25rem 0 1.5rem;
}

/* "Từ" / "From" — a lead-in label, not part of the price itself, so it's
   sized down to match .room-price-lead__unit ("/đêm") on the other side of
   the number for a small-PRICE-small bookend look instead of competing with
   the number for visual weight. */
.room-price-lead__label {
  font-size: 0.65em;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  font-family: var(--font-sans);
}

.room-price-lead strong {
  /* Sans-serif instead of the page's display serif — Playfair Display's
     numerals read as uneven next to full-height letterforms at this size;
     Montserrat's tabular-feeling digits are already used everywhere else
     price appears on the site (cards, split sections) with no such
     complaint, so this keeps the number visually consistent site-wide. */
  font-family: var(--font-sans);
  color: var(--color-price);
  font-weight: 700;
}

.room-price-lead__unit {
  font-size: 0.65em;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  font-family: var(--font-sans);
}

/* "Giá giảm" (discount) before/after display — original price struck
   through and muted, discounted price left to inherit its surrounding
   element's normal (already-prominent) price styling. Used in the room
   card lead price, the price table, the room-finder popover results, and
   the Booking page sidebar price. */
.room-price-lead__original {
  font-family: var(--font-sans);
  font-size: 0.55em;
  font-weight: 500;
  color: var(--color-text-muted);
  text-decoration: line-through;
  margin-right: 0.15em;
}

.price-original {
  color: var(--color-text-muted);
  text-decoration: line-through;
  font-weight: 400;
}

.price-discount {
  color: var(--color-price);
}

.room-filter-result__original {
  color: var(--color-text-muted);
  text-decoration: line-through;
  font-weight: 400;
}

.room-cta {
  align-self: flex-start;
}

/* Cover photo is clickable to open the full gallery — same affordance as a
   button so it doesn't read as a dead image. */
.split--room .split__image {
  cursor: pointer;
}

/* Shows exactly 4 thumbnails at a time — with more than 4 photos, the strip
   scrolls horizontally via swipe/trackpad/drag, with a soft fade + small
   arrow hint at whichever edge has more photos (instead of a visible
   scrollbar, which reads as a raw browser control rather than a designed
   part of the page). A sliver of the 5th thumbnail peeks in in at the edge
   as a quiet "there's more" cue even before the fade appears. */
.room-gallery-strip-wrap {
  position: relative;
  margin: -0.75rem 0 1.25rem;
  max-width: calc(56px * 4 + 0.75rem * 4 + 18px);
}

.room-gallery-strip {
  display: flex;
  gap: 0.75rem;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
}

.room-gallery-strip::-webkit-scrollbar { display: none; }

.room-gallery-fade {
  position: absolute;
  top: 0;
  width: 30px;
  height: 56px;
  display: flex;
  align-items: center;
  border: none;
  padding: 0;
  margin: 0;
  font-size: 0.8rem;
  color: var(--color-accent-warm);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
  z-index: 1;
}

.room-gallery-fade--left {
  left: 0;
  justify-content: flex-start;
  background: linear-gradient(to right, var(--color-bg-soft) 45%, transparent);
}

.room-gallery-fade--right {
  right: 0;
  justify-content: flex-end;
  background: linear-gradient(to left, var(--color-bg-soft) 45%, transparent);
}

.room-gallery-strip-wrap.can-scroll-left .room-gallery-fade--left,
.room-gallery-strip-wrap.can-scroll-right .room-gallery-fade--right {
  opacity: 1;
  pointer-events: auto;
}

.room-gallery-thumb {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  background-size: cover;
  background-position: center;
  background-color: var(--color-bg-soft);
  cursor: pointer;
  opacity: 0.65;
  transition: opacity 0.2s;
}

.room-gallery-thumb:hover,
.room-gallery-thumb.is-active {
  opacity: 1;
}

.price-table__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
}

.room-view-photos {
  background: none;
  border: none;
  padding: 0;
  color: var(--color-text-muted);
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  white-space: nowrap;
}

.room-view-photos:hover { color: var(--color-accent-warm); }

/* "Xem bảng giá & thông tin chi tiết" trigger — sits right under the "Đặt
   phòng" button inside the tier's own text column. On mobile this still
   expands inline via the native <details> (container-type lets the price
   table switch to its stacked mobile-style layout based on this column's
   own narrow width). On desktop (≥900px) main.js intercepts the click and
   opens .detail-modal instead, so [open] is never actually set there —
   .is-modal-open is toggled alongside it purely so the +/× icon still
   gives feedback while the popup is open. */
.room-detail {
  margin-top: 1.5rem;
  border-top: 1px solid var(--color-border);
  container-type: inline-size;
}

@media (min-width: 900px) {
  /* Pins the trigger to the bottom edge of the (now fixed-height, see
     .split--room) showcase box — margin-top:auto eats whatever slack is
     left after the sell copy above it, instead of that slack being split
     awkwardly above and below a vertically-centered block. Still at least
     1.5rem below the "Đặt phòng" button in every current category. */
  .split--room .room-detail {
    margin-top: auto;
  }
}

.room-detail summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.9rem 0;
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
}

.room-detail summary::-webkit-details-marker { display: none; }

.room-detail summary::after {
  content: '+';
  margin-left: auto;
  font-size: 1.15rem;
  transition: transform 0.3s var(--ease);
}

.room-detail[open] summary::after,
.room-detail.is-modal-open summary::after {
  transform: rotate(45deg);
}

.room-detail__inner {
  padding: 0 0 0.5rem;
}

.room-meta {
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  margin: 0 0 1.5rem;
}

.room-tier-subtitle {
  font-family: var(--font-sans);
  font-size: 0.55em;
  letter-spacing: 0.08em;
  color: var(--color-accent-warm);
  text-transform: uppercase;
  vertical-align: middle;
}

.price-table-wrap {
  overflow-x: auto;
}

.price-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8rem;
}

.price-table th,
.price-table td {
  text-align: left;
  padding: 0.85rem 0.75rem;
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
}

.price-table th {
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
  font-weight: 500;
  white-space: nowrap;
}

.price-table td:first-child,
.price-table th:first-child { padding-left: 0; }

/* Compact icon-only amenities row shown directly on the room card (below
   "Đặt phòng", above the price-detail trigger) — a quick-glance summary
   without opening the popup. Sized tight on purpose: the card's text
   column is a fixed height on desktop (see .split--room below) and the
   free space left between the CTA button and the detail trigger is only
   ~30px for the tightest categories (Standard/Family) — this row must fit
   inside that budget without pushing the trigger out of its bottom-anchored
   position or growing the card. Kept on both mobile and desktop since only
   desktop has the fixed-height constraint; mobile just gets a bit taller,
   which is harmless there. */
.room-amenities-mini {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.5rem;
}

.room-amenities-mini__item {
  display: flex;
  color: var(--color-text-muted);
  cursor: default;
}

.room-amenities-mini__item svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* Basic amenities list (same across every hạng phòng except balcony,
   which only Deluxe/Standard have) — sits below the price table in both
   the mobile inline <details> and the desktop .detail-modal popup. Reuses
   the pre-existing .amenities list style (see [data-theme="light"] override
   near the top of this file) rather than the checkmark-grid look common on
   OTA sites, to stay consistent with the site's minimal typographic voice. */
.room-amenities {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--color-border);
}

.room-amenities__title {
  font-family: var(--font-sans);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
  font-weight: 500;
  margin: 0;
}

.price-table__sub {
  display: block;
  font-size: 0.72rem;
  color: var(--color-text-muted);
  margin-top: 0.2rem;
}

.price-table .btn { white-space: nowrap; }

/* Stacks into a card-per-row layout once the price table's own column
   (i.e. .room-detail, see container-type above) gets narrow — a container
   query rather than a viewport media query because since the pricing
   detail moved inside the tier's text column, that column can be narrow
   even on a wide desktop screen (a fixed viewport breakpoint alone
   wouldn't catch that case). */
@container (max-width: 480px) {
  .price-table thead { display: none; }
  .price-table, .price-table tbody, .price-table tr, .price-table td { display: block; width: 100%; }
  .price-table tr {
    border-bottom: 1px solid var(--color-border);
    padding: 1rem 0;
  }
  .price-table td {
    border-bottom: none;
    padding: 0.3rem 0;
  }
  .price-table td[data-label]:not([data-label=""])::before {
    content: attr(data-label);
    display: block;
    font-size: 0.6rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-accent-warm);
    margin-bottom: 0.15rem;
  }
  .price-table td:last-child { padding-top: 0.75rem; }
  .price-table__actions { align-items: flex-start; }
}

/* Rooms page: policies — collapsed by default ("thu gọn") so the Rooms page
   stays shorter; opening it (or landing via the footer's deep link) reveals
   the full detail. */
.room-policies { max-width: 880px; margin: 0 auto; text-align: left; }

.room-policies__summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  text-align: center;
}

.room-policies__summary::-webkit-details-marker { display: none; }

.room-policies__summary::after {
  content: '+';
  font-size: 1.5rem;
  color: var(--color-accent-warm);
  transition: transform 0.3s var(--ease);
  flex-shrink: 0;
}

.room-policies[open] .room-policies__summary::after { transform: rotate(45deg); }

.room-policies h2 { font-family: var(--font-serif); font-size: clamp(1.75rem, 1.3rem + 2vw, 2.25rem); text-align: center; }

.room-policies__body { padding-top: 1rem; }

.room-policies__note {
  text-align: center;
  color: var(--color-text-muted);
  font-size: 0.85rem;
  margin: 1rem 0 3rem;
}

.policy-groups {
  display: grid;
  gap: 2rem;
  margin-bottom: 2.5rem;
}

@media (min-width: 768px) {
  .policy-groups { grid-template-columns: repeat(3, 1fr); }
}

.policy-group h4 {
  font-family: var(--font-serif);
  font-size: 1.1rem;
  margin-bottom: 0.85rem;
  color: var(--color-accent-warm);
}

.policy-group ul { list-style: none; }

.policy-group li {
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--color-text-muted);
  margin-bottom: 0.6rem;
  padding-left: 1rem;
  position: relative;
}

.policy-group li::before {
  content: '·';
  position: absolute;
  left: 0;
  color: var(--color-accent-warm);
}

.policy-contacts {
  border-top: 1px solid var(--color-border);
  padding-top: 1.5rem;
  text-align: center;
}

.policy-contacts p {
  font-size: 0.85rem;
  margin-bottom: 0.4rem;
}

.policy-contacts a:hover { opacity: 0.7; }

/* Lightbox — full-size photo viewer, shared by any page with a gallery
   (currently rooms). Injected into the DOM on first use by main.js, so it
   always starts hidden and only appears once a gallery photo is clicked. */
body.lightbox-open { overflow: hidden; }

.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(10, 10, 10, 0.94);
  align-items: center;
  justify-content: center;
}

.lightbox.is-open { display: flex; }

.lightbox__stage {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(2.5rem, 6vw, 5rem);
}

.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.lightbox__close,
.lightbox__nav {
  position: absolute;
  background: none;
  border: none;
  color: #f5f2ec;
  cursor: pointer;
  line-height: 1;
  opacity: 0.8;
  transition: opacity 0.2s;
}

.lightbox__close:hover,
.lightbox__nav:hover { opacity: 1; }

.lightbox__close {
  top: clamp(0.75rem, 3vw, 1.75rem);
  right: clamp(0.75rem, 3vw, 1.75rem);
  font-size: 2rem;
}

.lightbox__nav {
  top: 50%;
  transform: translateY(-50%);
  font-size: 3rem;
  padding: 0.5rem 0.75rem;
}

.lightbox__nav--prev { left: clamp(0.25rem, 2vw, 1.5rem); }
.lightbox__nav--next { right: clamp(0.25rem, 2vw, 1.5rem); }

.lightbox__count {
  position: absolute;
  bottom: clamp(0.75rem, 3vw, 1.75rem);
  left: 50%;
  transform: translateX(-50%);
  color: rgba(245, 242, 236, 0.75);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
}

/* Optional credit bar — only Feedback passes a caption into openLightbox(),
   so this stays empty/hidden for rooms/experiences and nothing changes
   there. Deliberately a short, steep gradient (clear by ~40% up from the
   bottom) rather than the card's taller one — zooming in is about seeing
   the photo, so only enough darkening for the name/stars to stay legible,
   no quote here at all. When a caption is present the "1/3" count moves up
   next to the close button so the two don't collide at the bottom. */
.lightbox__caption {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  align-items: center;
  gap: 0.75rem;
  padding: 2.25rem clamp(1.25rem, 4vw, 3rem) 1.1rem;
  background: linear-gradient(to top, rgba(8, 8, 8, 0.85) 0%, rgba(8, 8, 8, 0.45) 40%, rgba(8, 8, 8, 0) 75%);
  pointer-events: none;
}

.lightbox.has-caption .lightbox__caption { display: flex; }

.lightbox.has-caption .lightbox__count {
  bottom: auto;
  top: clamp(0.75rem, 3vw, 1.75rem);
  left: auto;
  right: clamp(3.25rem, 8vw, 4.75rem);
  transform: none;
}

.lightbox__caption-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
  background-color: rgba(255, 255, 255, 0.15);
  border: 1.5px solid rgba(255, 255, 255, 0.55);
  flex-shrink: 0;
}

.lightbox__caption-text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
  flex: 1;
}

.lightbox__caption-name {
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  font-weight: 500;
  color: #ffffff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}

.lightbox__caption-meta {
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}

.lightbox__caption-stars {
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  color: #e8bc74;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.75), 0 2px 8px rgba(0, 0, 0, 0.55);
  flex-shrink: 0;
}

@media (max-width: 640px) {
  .lightbox__nav { font-size: 2.25rem; padding: 0.4rem 0.5rem; }
  .lightbox__close { font-size: 1.6rem; }
  .lightbox.has-caption .lightbox__count { right: 3rem; }
  .lightbox__caption { padding-top: 3.25rem; gap: 0.5rem; }
  .lightbox__caption-avatar { width: 28px; height: 28px; }
}

/* Room detail popup ("Xem bảng giá & thông tin chi tiết") — desktop only
   (main.js only ever opens this at ≥900px; on mobile the same content
   still expands inline via the native <details>). Same overlay pattern as
   .lightbox above for visual consistency, but the panel holds the price
   table rather than a photo, so it gets a comfortably wide, scrollable
   card instead of a full-bleed stage. Injected into the DOM on first use,
   so it stays absent from every page until a "Xem bảng giá" button is
   actually clicked. */
body.detail-modal-open { overflow: hidden; }

.detail-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(10, 10, 10, 0.7);
  align-items: center;
  justify-content: center;
  padding: clamp(1.25rem, 4vw, 3rem);
}

.detail-modal.is-open { display: flex; }

.detail-modal__panel {
  position: relative;
  width: min(900px, 100%);
  max-height: 85vh;
  overflow-y: auto;
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  padding: clamp(1.75rem, 4vw, 3rem);
  /* The price table can size itself off the panel's own generous width
     instead of the narrow text column it lives in on mobile — the same
     @container rule (main.css, near .price-table) just never triggers at
     this width, so the table shows its full multi-column layout. */
  container-type: inline-size;
}

.detail-modal__close {
  position: absolute;
  top: clamp(0.75rem, 3vw, 1.5rem);
  right: clamp(0.75rem, 3vw, 1.5rem);
  background: none;
  border: none;
  color: var(--color-text);
  font-size: 1.6rem;
  line-height: 1;
  opacity: 0.6;
  cursor: pointer;
  transition: opacity 0.2s;
}

.detail-modal__close:hover { opacity: 1; }

.detail-modal__title {
  padding-right: 2rem;
  margin: 0 0 1.5rem;
}

.detail-modal__body .room-detail__inner {
  padding: 0;
}

/* Room filter ("Tìm phòng phù hợp") — a small icon in the corner of each
   room-showcase box that opens a compact popover with guests/rooms/size/
   dates, and recommends the cheapest loại phòng con that fits (main.js:
   computeRoomRecommendation). Pure static-data math: there's no live
   booking calendar yet, so the dates only ever affect the PRICE estimate
   (weekday vs weekend nights) — never whether a room is actually free.
   Every result says so explicitly (.room-filter-result__note). */
.room-filter-trigger {
  position: absolute;
  top: 1rem;
  right: 1rem;
  z-index: 5;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  color: var(--color-accent-warm);
  cursor: pointer;
  transition: background 0.2s;
}

.room-filter-trigger:hover { background: var(--color-border); }
.room-filter-trigger svg { width: 16px; height: 16px; }

/* Mobile-first: a centered fixed overlay, same pattern as .lightbox/
   .detail-modal (a small icon anchored to a corner popover doesn't have
   room to breathe on a narrow screen). Desktop overrides this below to an
   anchored dropdown-style card instead, right under the trigger icon. */
.room-filter-popover {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 210;
  background: rgba(10, 10, 10, 0.7);
  align-items: center;
  justify-content: center;
  padding: clamp(1.25rem, 4vw, 3rem);
}

.room-filter-popover.is-open { display: flex; }

.room-filter-popover__panel {
  position: relative;
  width: min(360px, 100%);
  max-height: 85vh;
  overflow-y: auto;
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  padding: 1.5rem;
}

@media (min-width: 900px) {
  .room-filter-popover {
    display: block;
    position: absolute;
    inset: auto;
    top: 3.5rem;
    right: 1rem;
    z-index: 6;
    background: none;
    padding: 0;
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s;
  }

  .room-filter-popover.is-open {
    visibility: visible;
    opacity: 1;
    pointer-events: auto;
  }

  .room-filter-popover__panel {
    width: 380px;
    max-height: none;
    padding: 1.75rem;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
  }

  .room-filter-popover__title { font-size: 0.74rem; margin-bottom: 1.3rem; }
  .room-filter-field { margin-bottom: 1rem; }
  .room-filter-field label { font-size: 0.65rem; margin-bottom: 0.4rem; }
  .room-filter-field select,
  .room-filter-field input[type='date'] { padding: 0.65rem 0.7rem; font-size: 0.85rem; }
  .room-filter-row { gap: 0.85rem; }
  .room-filter-result { font-size: 0.85rem; margin-top: 1.1rem; padding-top: 1.1rem; }

  /* .room-showcase--reverse flips which side the image sits on (see
     .room-showcase--reverse .split__image { order: 2 } below) — on those
     rows the photo is on the right, so the trigger/popover must flip to
     the left (over the text panel) instead of sitting on top of the photo. */
  .room-showcase--reverse .room-filter-trigger { right: auto; left: 1rem; }
  .room-showcase--reverse .room-filter-popover { right: auto; left: 1rem; }
}

.room-filter-popover__close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  background: none;
  border: none;
  color: var(--color-text);
  font-size: 1.3rem;
  line-height: 1;
  opacity: 0.6;
  cursor: pointer;
}

.room-filter-popover__close:hover { opacity: 1; }

.room-filter-popover__title {
  font-family: var(--font-sans);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
  margin: 0 1.5rem 1.1rem 0;
}

.room-filter-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.65rem;
}

/* Grid items default to min-width:auto, which lets a native date input's
   intrinsic content width (text + calendar-icon) push it past its 1fr
   track and overflow the popover's right edge — min-width:0 lets it
   actually shrink to fit like a normal block element would. */
.room-filter-row > .room-filter-field { min-width: 0; }

.room-filter-field { margin-bottom: 0.8rem; }

.room-filter-field label {
  display: block;
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: 0.3rem;
}

.room-filter-field select,
.room-filter-field input[type='date'] {
  width: 100%;
  padding: 0.5rem 0.55rem;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  color: var(--color-text);
  font-family: inherit;
  font-size: 0.78rem;
}

/* The native date-picker icon (calendar glyph) is rendered by the browser,
   not drawn by us — by default it's a dark/black icon, which disappears
   against our dark theme's near-black input background. color-scheme tells
   the browser which palette its own native controls (this icon included)
   should render in, so it switches to a light icon automatically — no need
   to fake it with an invert filter, and it stays correct if the input ever
   sits on a light background too. */
.room-filter-field input[type='date'] { color-scheme: dark; }
[data-theme="light"] .room-filter-field input[type='date'] { color-scheme: light; }

.room-filter-result {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--color-border);
  font-size: 0.78rem;
  line-height: 1.5;
}

.room-filter-result:empty { display: none; }
.room-filter-result p { margin: 0 0 0.5rem; }
.room-filter-result__pick { color: var(--color-text); }

.room-filter-result__warn {
  color: #b3423f;
}

.room-filter-result__note {
  font-size: 0.68rem;
  color: var(--color-text-muted);
  margin-bottom: 0 !important;
}

.room-filter-result__cta {
  display: inline-block;
  margin-bottom: 0.6rem;
}

.room-filter-result__bump {
  background: none;
  border: 1px solid var(--color-border);
  padding: 0.5rem 0.75rem;
  font-size: 0.72rem;
  color: var(--color-accent-warm);
  cursor: pointer;
}

.room-filter-result__bump:hover { background: var(--color-border); }

/* Booking page's own version of the same "Tìm phòng phù hợp" widget (see
   the .room-filter-* rules above, which this reuses as-is for the popover
   itself — .room-filter-popover/__panel/__title/__close, .room-filter-row/
   __field, .room-filter-result*). Booking has no per-category card to
   pin a small corner icon to (unlike .room-showcase on the Rooms page), so
   the trigger here is a full-width text+icon button instead of
   .room-filter-trigger's absolutely-positioned icon, and needs its own
   `position: relative` wrapper for the popover to anchor against on
   desktop (see the `top: 3.5rem; right: 1rem` rule on .room-filter-popover
   above — it positions relative to the nearest positioned ancestor,
   .room-showcase there, .booking-filter-anchor here). */
.booking-filter-anchor {
  position: relative;
}

.booking-filter-trigger {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  color: var(--color-accent-warm);
  font-family: var(--font-sans);
  font-size: 0.8rem;
  padding: 0.85rem 1rem;
  cursor: pointer;
  transition: background 0.2s;
}

.booking-filter-trigger:hover { background: var(--color-border); }
.booking-filter-trigger svg { width: 18px; height: 18px; flex-shrink: 0; }

/* FAQ page */
.faq-list {
  max-width: 760px;
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 3rem);
}

.faq-item {
  border-bottom: 1px solid var(--color-border);
}

.faq-item summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem 0;
  font-family: var(--font-serif);
  font-size: 1.05rem;
}

.faq-item summary::-webkit-details-marker { display: none; }

.faq-item summary::after {
  content: '+';
  margin-left: auto;
  font-size: 1.3rem;
  color: var(--color-accent-warm);
  transition: transform 0.3s var(--ease);
  flex-shrink: 0;
}

.faq-item[open] summary::after { transform: rotate(45deg); }

.faq-item p {
  color: var(--color-text-muted);
  line-height: 1.8;
  padding-bottom: 1.5rem;
  margin: 0;
}

/* Forms */
.form-section {
  max-width: 560px;
  margin: 0 auto;
  padding: 0 1.5rem 5rem;
}

.form-group { margin-bottom: 1.5rem; }

.form-group label {
  display: block;
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
  color: var(--color-text-muted);
}

.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 0.9rem 1rem;
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  font-family: inherit;
  font-size: 0.9rem;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--color-accent);
}

/* Chrome/Edge draw the type="date" calendar icon as a plain dark-gray glyph
   with no theme awareness of their own — on light mode's pale input
   background that's fine, but on dark mode's near-black input background
   (--color-bg-soft) it's nearly invisible. `invert()` flips it to light,
   `opacity` keeps it from reading as pure white/too loud next to the
   muted-gray field text. Firefox doesn't expose this pseudo-element; its
   icon is already theme-aware, so this simply doesn't apply there.
   Scoped to a DIRECT child of .form-group (the main booking/contact form
   fields) ONLY, via the `>` combinator — NOT a descendant selector — for a
   subtle reason: the Booking page's "Tìm phòng phù hợp" trigger+popover is
   itself wrapped in a `<div class="form-group booking-filter-anchor">` (so
   it lines up with the other form fields), which means its NESTED
   .room-filter-field date inputs are technically descendants of a
   .form-group too. A plain descendant selector (`.form-group input[...]`)
   would match them and double up with `color-scheme: dark` above — which
   already gives them a light icon for free — flipping it back to dark and
   making it disappear again against the dark input background (see
   21/8/2026 fix #1, which used a descendant selector and still had this
   bug for exactly that reason). `>` fixes it because #checkin/#checkout are
   direct children of their .form-group, while the popover's date inputs
   sit several levels deeper (.form-group > .room-filter-popover > ... >
   .room-filter-field > input), so they no longer match here. */
[data-theme="dark"] .form-group > input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.7;
}
[data-theme="dark"] .form-group > input[type="date"]::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
}

.form-note {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin-bottom: 2rem;
  text-align: center;
}

.form-success {
  padding: 1rem;
  border: 1px solid var(--color-accent);
  text-align: center;
  margin-bottom: 1.5rem;
  display: none;
}

.form-success.show { display: block; }

.form-error {
  padding: 1rem;
  border: 1px solid #b3423f;
  color: #b3423f;
  text-align: center;
  margin-bottom: 1.5rem;
  display: none;
}

.form-error.show { display: block; }

/* Shown only if content/{vi,en}.json fails to load even after a retry (see
   init() in main.js) — a rare, genuine failure rather than the everyday
   case, so this deliberately isn't styled to blend in like a normal page
   section. */
.content-load-error {
  max-width: 480px;
  margin: 4rem auto;
  padding: 2rem;
  text-align: center;
  border: 1px solid #b3423f;
  border-radius: 4px;
  color: #b3423f;
}

.form-group button[disabled],
button.btn[disabled] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Footer */
.site-footer {
  padding: 4rem clamp(1.5rem, 5vw, 4rem) 2rem;
  border-top: 1px solid var(--color-border);
}

.footer-grid {
  display: grid;
  gap: 3rem;
  max-width: 1200px;
  margin: 0 auto 3rem;
  text-align: center;
}

@media (min-width: 768px) {
  .footer-grid {
    grid-template-columns: 1.4fr 1fr 1fr 1fr;
    text-align: left;
  }
}

.site-footer .logo { margin-bottom: 1.5rem; display: inline-block; }

.footer-info {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  line-height: 2.1;
}

.footer-col h5 {
  font-size: 0.75rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
  font-weight: 500;
  margin-bottom: 1.25rem;
}

.footer-col ul { list-style: none; }

.footer-col li { margin-bottom: 0.85rem; }

.footer-col a {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  transition: color 0.2s;
}

.footer-col a:hover { color: var(--color-text); }

.footer-social {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
}

@media (min-width: 768px) {
  .footer-social { justify-content: flex-start; }
}

.footer-social__link,
.contact-info__social-link {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  color: var(--color-text-muted);
  transition: border-color 0.2s, color 0.2s;
}

.footer-social__link svg,
.contact-info__social-link svg { width: 1.1rem; height: 1.1rem; }

a.footer-social__link:hover,
a.contact-info__social-link:hover { border-color: var(--color-accent); color: var(--color-text); }

.footer-social__link.is-placeholder,
.contact-info__social-link.is-placeholder { opacity: 0.4; cursor: default; }

.footer-copy {
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--color-text-muted);
  text-align: center;
}

/* Amenities */
.amenities {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-top: 1rem;
}

.amenities li {
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--color-text-muted);
}

.amenities li::before { content: '· '; color: var(--color-accent-warm); }

.split__text .price,
.text-accent-warm {
  color: var(--color-accent-warm);
}

/* Testimonials (Feedback page) — image-forward, continuously auto-scrolling
   row (replaces the earlier static text-forward grid). Photos dominate each
   card; name/quote/rating sit in a gradient overlay at the bottom, the same
   visual language as .card/.card__overlay used for room/dining teasers.
   The track is a native horizontally-scrollable element — auto-scroll,
   pause-on-interact and drag-to-scroll are driven by JS (initTestimonialSlider
   in main.js); this file only handles sizing/appearance. */
.testimonial-slider {
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 3rem);
}

.testimonial-track {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  scroll-behavior: auto;
  cursor: grab;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.testimonial-track:active { cursor: grabbing; }
.testimonial-track::-webkit-scrollbar { display: none; }

/* One large photo per slide — flex-basis controls how many are visible at
   once: ~1 (peeking the next) on phones, ~1.5 on tablets, 3 on desktop.
   Widened per explicit request after testing on a phone: at the old 640px
   tier's 46%, two full cards fit side by side, each quite narrow — since
   the review-text overlay at the bottom (stars/quote/avatar/name/stay info)
   is a roughly fixed block, that left very little of the photo actually
   visible above it on a narrow card. A wider card (same 4:5 aspect ratio)
   is proportionally taller too, so the same overlay block now covers much
   less of it. */
.testimonial-slide {
  position: relative;
  flex: 0 0 86%;
  aspect-ratio: 4/5;
  overflow: hidden;
  background: var(--color-bg-soft);
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

/* Subtle lift on hover — paired with main.js only slowing the auto-scroll
   down on hover instead of stopping it dead, so the row still feels like
   it's gently moving rather than "freezing". overflow:hidden above still
   clips the photo inside as normal; it doesn't clip this shadow, since a
   box-shadow paints outside the box's own border edge. */
.testimonial-slide:hover {
  transform: translateY(-6px) scale(1.015);
  box-shadow: 0 20px 45px rgba(0, 0, 0, 0.32);
  z-index: 2;
}

@media (min-width: 640px) {
  .testimonial-slide { flex-basis: 64%; }
}

@media (min-width: 1024px) {
  .testimonial-slide { flex-basis: 31.5%; }
}

.testimonial-slide__img {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

/* Custom expand-arrows cursor instead of the browser's default zoom-in
   magnifying glass — a small dark circle badge with two diagonal arrows,
   subtle enough to read on both bright and dark photos. Falls back to the
   native zoom-in cursor on the rare browser that can't load an SVG cursor. */
.testimonial-slide[data-zoomable] {
  cursor: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMCIgaGVpZ2h0PSIzMCIgdmlld0JveD0iMCAwIDMwIDMwIj4KPGNpcmNsZSBjeD0iMTUiIGN5PSIxNSIgcj0iMTMiIGZpbGw9InJnYmEoMTAsMTAsMTAsMC41NSkiLz4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUsMTUpIHNjYWxlKDAuNjIpIHRyYW5zbGF0ZSgtMTIsLTEyKSI+Cjxwb2x5bGluZSBwb2ludHM9IjE1IDMgMjEgMyAyMSA5IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjxwb2x5bGluZSBwb2ludHM9IjkgMjEgMyAyMSAzIDE1IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxsaW5lIHgxPSIyMSIgeTE9IjMiIHgyPSIxNCIgeTI9IjEwIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxsaW5lIHgxPSIzIiB5MT0iMjEiIHgyPSIxMCIgeTI9IjE0IiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjwvZz4KPC9zdmc+') 15 15, zoom-in;
}

/* Extra trip photos beyond the hero one — a small badge, not a new popup;
   clicking it opens the same lightbox already used for room/experience
   photos, starting from the first photo. */
.testimonial-slide__count {
  position: absolute;
  top: 0.85rem;
  right: 0.85rem;
  z-index: 1;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  border: none;
  padding: 0.3rem 0.65rem;
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: background 0.2s var(--ease);
}

.testimonial-slide__count:hover,
.testimonial-slide__count:focus-visible {
  background: rgba(0, 0, 0, 0.8);
}

/* Deliberately a stronger, custom gradient rather than the shared
   --color-card-overlay used for dining/room teasers — this overlay has to
   carry star rating + a quote + name/avatar over a photo, and guest-uploaded
   trip photos are often bright/busy (greenery, sky, white walls) where the
   lighter --color-card-overlay tested unreadable. Text also gets its own
   text-shadow as a second line of defense, same fix already used for hero
   titles and the Experiences popup's photo counter.
   The gradient is deliberately steep and concentrated near the bottom
   (fully clear again by 72% up) rather than spread across the whole photo —
   an earlier version faded gradually from 100% down to 0%, which darkened
   roughly two-thirds of every guest photo even where no text sat, burying
   the photo the whole feature is meant to showcase. Keep this tight to the
   actual text footprint whenever that footprint changes. */
.testimonial-slide__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(8, 8, 8, 0.95) 0%,
    rgba(8, 8, 8, 0.88) 22%,
    rgba(8, 8, 8, 0.55) 42%,
    rgba(8, 8, 8, 0.15) 60%,
    rgba(8, 8, 8, 0) 74%
  );
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.4rem;
  padding: clamp(1rem, 2.5vw, 1.5rem);
}

/* Every overlay text color below is a fixed value + !important, deliberately
   NOT any of the --color-on-hero(-muted)/--color-accent-warm variables. Two
   real bugs drove this:
   1) `.testimonial-slide__quote` is a <p>, and this stylesheet has a broad
      "[data-theme="light"] .section p, ... { color: var(--color-text-muted);
      font-weight: 500; }" rule (near the top of the file) meant to darken
      body copy for readability on the light theme's pale page background.
      That rule's specificity (class+class+element, 0,2,1) beats even a
      two-class selector here, so it was silently painting the quote
      near-black — unreadable on a photo. Bumping selector specificity
      further would only invite the same collision again from some other
      future ".section ..." rule, so color is pinned with !important instead:
      "text sitting on a photo" and "text sitting on the page background"
      are different rendering contexts and must never share a color rule.
   2) --color-accent-warm is intentionally swapped per theme for TEXT ON THE
      PAGE BACKGROUND (dark gold in dark mode, a much darker brown in light
      mode so it stays readable on a pale page) — never meant for text over
      a photo, which stays dark behind it in both themes. Using it for the
      star rating meant light mode rendered near-black stars on a near-black
      overlay. Photo-overlay text needs ONE fixed color per role regardless
      of site theme — same reasoning as --color-on-hero already gets, just
      not consistently applied here originally. */
.testimonial-slide .testimonial-slide__stars {
  font-size: 0.95rem;
  letter-spacing: 0.15em;
  color: #e8bc74 !important;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.75), 0 2px 8px rgba(0, 0, 0, 0.55);
}

.testimonial-slide .testimonial-slide__quote {
  font-family: var(--font-serif);
  font-size: 1rem;
  line-height: 1.5;
  margin: 0;
  color: #ffffff !important;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7), 0 2px 10px rgba(0, 0, 0, 0.55);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.testimonial-slide__meta {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.testimonial-slide__avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
  background-color: rgba(255, 255, 255, 0.15);
  border: 1.5px solid rgba(255, 255, 255, 0.55);
  flex-shrink: 0;
}

.testimonial-slide__meta-text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.testimonial-slide .testimonial-slide__name {
  font-size: 0.82rem;
  letter-spacing: 0.05em;
  font-weight: 500;
  color: #ffffff !important;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}

.testimonial-slide .testimonial-slide__stay {
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82) !important;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}

/* "Coming soon" placeholder state (e.g. Dining page before it's ready) */
.coming-soon {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  max-width: 560px;
  margin: 0 auto;
  /* Sits inside .section, which already adds top/bottom padding — only add
     horizontal padding here so the two don't stack into a huge empty gap. */
  padding: 0 1.5rem;
  text-align: center;
}

.coming-soon h2 {
  font-family: var(--font-serif);
  font-size: clamp(1.75rem, 1.3rem + 2vw, 2.5rem);
}

.coming-soon p {
  color: var(--color-text-muted);
  line-height: 1.8;
}

/* Demo badge */
.demo-badge {
  position: fixed;
  bottom: 1rem;
  right: 1rem;
  z-index: 200;
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.4rem 0.75rem;
  background: var(--color-accent);
  color: var(--color-on-accent);
  opacity: 0.9;
}

@media (min-width: 1024px) {
  .nav-desktop { display: flex; }
  .menu-toggle { display: none; }
}

/* ===== About page (Aman Residences–inspired) =====
   Editorial hero (see .hero-content__label above) → large-format philosophy
   statement → a portrait-card grid of brand pillars → a clickable gallery
   strip → an optional host/GM note → location with a map link. Generous
   whitespace and portrait imagery over dense text, per the explicit
   aman.com/residences reference. */

/* Philosophy statement — same .section--narrow centering as elsewhere, just
   a slightly larger, more relaxed line-height so it reads as a deliberate
   editorial statement rather than ordinary body copy. */
.about-philosophy p {
  font-size: 1.02rem;
  line-height: 1.9;
}

/* Pillar grid — "Điều làm nên Việt Tín" */
.about-pillars__head {
  text-align: center;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.pillar-grid {
  display: grid;
  gap: clamp(2rem, 4vw, 3rem) clamp(1.5rem, 3vw, 2rem);
  max-width: 1320px;
  margin: 0 auto;
}

@media (min-width: 640px) {
  .pillar-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .pillar-grid { grid-template-columns: repeat(3, 1fr); }
}

.pillar-card__img {
  aspect-ratio: 3 / 4;
  background-size: cover;
  background-position: center;
  margin-bottom: 1.5rem;
}

.pillar-card__title {
  font-family: var(--font-serif);
  font-size: 1.25rem;
  font-weight: 400;
  margin-bottom: 0.6rem;
  color: var(--color-text);
}

.pillar-card__desc {
  font-size: 0.85rem;
  line-height: 1.75;
  color: var(--color-text-muted);
  margin: 0;
}

/* Gallery strip — a horizontally-scrolling row of ambience photos; each tile
   opens the shared lightbox (see openLightbox() in main.js) to page through
   the full set at full size. Native scroll + snap, no auto-scroll/rAF like
   the Feedback testimonial track — this row is much shorter and is meant to
   be browsed deliberately, not to run continuously in the background. */
.gallery-strip {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 0.5rem;
  max-width: 1320px;
  margin: 0 auto;
}

.gallery-strip__item {
  flex: 0 0 auto;
  width: clamp(160px, 20vw, 240px);
  aspect-ratio: 3 / 4;
  background-size: cover;
  background-position: center;
  scroll-snap-align: start;
  border: none;
  padding: 0;
  cursor: pointer;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.gallery-strip__item:hover,
.gallery-strip__item:focus-visible {
  transform: translateY(-6px);
  box-shadow: 0 18px 36px rgba(0, 0, 0, 0.18);
}

.gallery-strip__item:focus-visible {
  outline: 2px solid var(--color-accent-warm);
  outline-offset: 3px;
}

/* Host/GM note — a short, centred quote block on a soft background, closer
   to a pull-quote than a review card. Hidden entirely by renderAbout() in
   main.js when the admin hasn't written a message yet. */
.about-host {
  padding: clamp(4rem, 10vw, 7rem) clamp(1.5rem, 5vw, 4rem);
  background: var(--color-bg-soft);
  text-align: center;
}

.about-host__inner {
  max-width: 640px;
  margin: 0 auto;
}

.about-host__avatar {
  width: 88px;
  height: 88px;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
  margin: 0 auto 1.75rem;
  border: 1px solid var(--color-border);
}

.about-host__message {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: clamp(1.2rem, 1rem + 1vw, 1.55rem);
  line-height: 1.7;
  color: var(--color-text);
  margin: 0 0 1.5rem;
}

.about-host__name {
  display: block;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-text);
}

.about-host__role {
  display: block;
  font-size: 0.68rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--color-accent-warm);
  margin-top: 0.35rem;
}

/* Location — reuses .split/.split__image/.split__text; only the "Xem chỉ
   đường" map button is specific to these pages. The Contact page's location
   block (.contact-location, styled further down) shows the same photo and
   map link as this one — same selector so both stay visually identical.
   The homepage's own location block (.home-location, replacing the old
   Dining teaser there) reuses the same data and markup, so it gets the
   same button treatment too. */
.about-location .split__text .btn,
.contact-location .split__text .btn,
.home-location .split__text .btn {
  align-self: flex-start;
  margin-top: 0.5rem;
}

/* ===== Contact page =====
   A plain, photo-less title bar (see .hero--plain below — explicit
   request: with a large form photo and a location photo already on this
   page, a 3rd hero photo felt like too many) → a form/photo/info block →
   a location block identical in markup/behavior to the About page's own
   (see the comment above), so a guest ends up on this page for the
   message form but sees the exact same location info without it being
   entered twice.

   .hero--plain overrides the shared .hero/.hero--short rules above (same
   selector specificity, so source order decides — this block comes later
   in the file, which is what makes the override work): no hero-bg/overlay
   photo, a short auto-height bar instead of a near-full-viewport panel,
   and normal --color-text/--color-text-muted instead of
   --color-hero-text/--color-hero-text-muted (that pair is white-on-dark-
   photo by design — on a plain background, especially in light theme
   where the page itself is pale, white text would be unreadable). The
   text-shadow added elsewhere for legibility over a photo is turned off
   here for the same reason — nothing under the text to fight for
   contrast against. */
.hero--plain {
  min-height: 0;
  align-items: center;
  padding: calc(var(--header-h) + 3.5rem) 1.5rem 3rem;
  background: var(--color-bg-soft);
}

.hero--plain .hero-content { color: var(--color-text); }

.hero--plain .hero-content p,
.hero--plain .hero-content__label {
  color: var(--color-text-muted);
}

[data-theme="light"] .hero--plain .hero-content,
[data-theme="light"] .hero--plain .hero-content p,
[data-theme="light"] .hero--plain .hero-content__label {
  text-shadow: none;
}

/* The form/photo/info block's DOM order is photo → form → info, which is
   also the mobile stacking order the owner asked for. Below 900px this is
   a single-column grid, so it just stacks in that order with no extra CSS
   needed. At ≥900px, grid-column/grid-row placement re-lays it out into
   "form on top of info, both on the left; one large photo on the right
   spanning both rows" — the photo's grid-row span plus align-items:stretch
   is what makes it fill the full height of the form+info column instead of
   just matching whichever of the two is taller. */
.contact-layout__inner {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
  max-width: 1320px;
  margin: 0 auto;
  align-items: start;
}

@media (min-width: 900px) {
  .contact-layout__inner {
    grid-template-columns: 1fr 1.75fr;
    align-items: stretch;
  }
  .contact-form-wrap { grid-column: 1; grid-row: 1; }
  .contact-info { grid-column: 1; grid-row: 2; }
  .contact-layout__image { grid-column: 2; grid-row: 1 / 3; }
}

.contact-layout__image {
  min-height: 320px;
  background-size: cover;
  background-position: center;
}

.contact-info__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  margin: 1.5rem 0 2rem;
  padding: 0;
}

.contact-info__item-label {
  display: block;
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: 0.4rem;
}

.contact-info__item a {
  font-family: var(--font-serif);
  font-size: 1.2rem;
  color: var(--color-text);
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.contact-info__item a:hover { opacity: 0.75; }

/* Sized against the serif value text next to it (1.2rem), not the tiny
   uppercase label above — that's a deliberately different, smaller scale
   (0.65rem) the icon would dwarf if matched to it instead. */
.contact-info__item-icon {
  display: inline-flex;
  flex-shrink: 0;
}

.contact-info__item-icon svg {
  width: 17px;
  height: 17px;
  opacity: 0.75;
}

.contact-info__social { display: flex; gap: 0.75rem; }

.contact-form-wrap {
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  padding: clamp(2rem, 4vw, 3rem);
}

.contact-form-wrap h2 {
  font-family: var(--font-serif);
  font-size: 1.4rem;
  font-weight: 400;
  margin-bottom: 1.75rem;
}

.contact-form-wrap .form-group:last-of-type { margin-bottom: 2rem; }

/* ===== Booking page =====
   Was previously just a bare, flat 8-field form on a plain background —
   the least finished-looking page on the site. Redesigned into a form card
   (visually matching .contact-form-wrap, but its own class rather than a
   shared one — .contact-form-wrap/.contact-info carry grid-column/grid-row
   overrides scoped to .contact-layout__inner's specific 3-part layout, and
   reusing those classes here would have silently pulled that placement
   into this page's plain 2-column grid) beside a "Cần hỗ trợ đặt phòng?"
   sidebar reusing the Contact page's info-list styling (.contact-info__list
   etc. — those child classes carry no grid placement, so they're safe to
   share). The .contact-form-wrap → .booking-form-wrap split mirrors how
   .split is the shared generic base while page-specific cards
   (.pillar-card, .contact-form-wrap) stay page-scoped elsewhere in this
   file. Coming from the Rooms page with a specific room already chosen
   (via ?room=<id> — see the room-filter code in main.js), a summary card
   (.booking-room-summary) shows that room's photo/name/price up top
   instead of silently preselecting the <select> where it's easy to miss;
   "Đổi phòng khác" swaps it back out for the raw dropdown. */
.booking-layout__inner {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
  max-width: 1100px;
  margin: 0 auto;
  align-items: start;
}

@media (min-width: 900px) {
  .booking-layout__inner { grid-template-columns: 1.6fr 1fr; }
}

.booking-form-wrap {
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  padding: clamp(2rem, 4vw, 3rem);
}

.booking-group-heading {
  font-family: var(--font-serif);
  font-size: 1rem;
  font-weight: 400;
  margin: 0 0 1.25rem;
}

.booking-group-heading:not(:first-child) {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--color-border);
}

.booking-room-summary {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  border: 1px solid var(--color-border);
  padding: 1rem;
  margin-bottom: 2rem;
}

/* Without this, the [hidden] attribute set/cleared from renderBooking() in
   main.js would silently do nothing — the browser's own [hidden] {
   display: none } UA rule and this file's .booking-room-summary { display:
   flex } rule have equal specificity, and this file loads after the UA
   stylesheet, so display:flex would always win and the card would stay
   visible even while actually hidden. */
.booking-room-summary[hidden] { display: none; }

.booking-room-summary__img {
  width: 100px;
  height: 100px;
  flex: 0 0 auto;
  background-size: cover;
  background-position: center;
}

.booking-room-summary__label {
  display: block;
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: 0.3rem;
}

.booking-room-summary__body h3 {
  font-family: var(--font-serif);
  font-size: 1.1rem;
  font-weight: 400;
  margin: 0 0 0.3rem;
}

.booking-room-summary__body p {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0 0 0.6rem;
}

.booking-room-summary__change {
  background: none;
  border: none;
  padding: 0;
  font-size: 0.75rem;
  text-decoration: underline;
  color: var(--color-accent-warm);
  cursor: pointer;
  font-family: inherit;
}

/* The sidebar originally had nothing in it but the "Cần hỗ trợ đặt phòng?"
   text block — no background/border at all — so next to the much taller
   .booking-form-wrap card it read as a bare void (flagged by user
   screenshot review — 18/8/2026). A first pass just gave that lone block a
   matching card background + sticky position, but that was a cosmetic
   patch, not real content — user feedback (same day) asked for the space
   to actually be used: a room photo + included amenities panel, with the
   help block underneath it. .booking-sidebar is the actual grid child now
   (holds both cards); .booking-room-info is the new photo/amenities card;
   .booking-help is unchanged in role, just nested one level deeper.
   Originally also had `position: sticky` on desktop so this column stayed
   pinned while the taller form column scrolled past — tested clean (no
   overlap with the footer in either the "room preset" or "no room chosen"
   state, verified by measuring bounding boxes across a full scroll pass),
   but it relies on an implicit invariant (form column always taller than
   this one) that a future content/copy change could quietly break, and the
   two columns visibly scrolling at different rates read as a bug rather
   than a feature to a user watching it happen (flagged 19/8/2026) — so it
   was removed in favor of plain static positioning; the column now simply
   scrolls in place with the rest of the page like everywhere else on this
   site. */
.booking-sidebar {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.booking-room-info {
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  overflow: hidden;
}

.booking-room-info__placeholder {
  margin: 0;
  padding: clamp(1.75rem, 3vw, 2.25rem);
  font-size: 0.85rem;
  line-height: 1.7;
  color: var(--color-text-muted);
}

/* Mirrors the [hidden]-override caution documented on .booking-room-summary
   above — neither element here sets a conflicting `display` today, but the
   rule is kept for the same defensive reason: a later style tweak that adds
   one would otherwise silently break the show/hide toggle in main.js. */
.booking-room-info__placeholder[hidden],
.booking-room-info__content[hidden] {
  display: none;
}

.booking-room-info__img {
  aspect-ratio: 4 / 3;
  background-size: cover;
  background-position: center;
}

.booking-room-info__body {
  padding: clamp(1.5rem, 3vw, 2rem);
}

.booking-room-info__body h3 {
  font-family: var(--font-serif);
  font-size: 1.1rem;
  font-weight: 400;
  margin: 0 0 0.35rem;
}

/* Made bigger/bolder per explicit request (21/8/2026) — the room name (h3,
   1.1rem/weight 400 serif) was visually louder than the price sitting right
   under it, when the price is the more actionable number for someone
   comparing rooms. Targeted by #id, not just .booking-room-info__price —
   this element is a <p> inside .section (the page's outer wrapper), and
   light mode has a broad `[data-theme="light"] .section p { font-weight:
   500 }` rule (toning down body-text paragraphs sitewide) whose specificity
   (two classes + a tag) beats a single class selector here, silently
   overriding the 700 back to 500 in light mode only. The #id selector
   outranks that combination outright regardless of theme. */
#booking-room-info-price {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--color-accent-warm);
  margin: 0;
}

/* .room-amenities / .amenities are reused as-is from the Rooms page detail
   popup (see main.js renderRooms) — that component carries no grid/layout
   placement rules, only text styling, so it's safe to share here per the
   class-reuse convention established elsewhere on this page. */
.booking-room-info__body .room-amenities { margin-top: 1.25rem; padding-top: 1.25rem; }

.booking-help {
  background: var(--color-bg-soft);
  border: 1px solid var(--color-border);
  padding: clamp(1.75rem, 3vw, 2.25rem);
}

.booking-help__note {
  font-size: 0.8rem;
  line-height: 1.7;
  color: var(--color-text-muted);
  margin-top: 1.5rem;
}
