/* ==========================================================================
   Design tokens
   ========================================================================== */
:root {
  /* Sotheby's International Realty brand blue (Pantone 289 / #002349),
     used as the site's primary navy, with a darker tint for full-bleed
     bands like the hero bar and footer. */
  --color-navy: #002349;
  --color-navy-deep: #001830;
  /* Same hue and saturation as --color-navy, just lighter — so every
     blue on the page reads as one family at different depths, not as
     separate colours. */
  --color-steel: #215083;
  --color-steel-light: #71a1d6;
  /* A third step between --color-steel and --color-steel-light, filling
     the gap in that same blue scale. Used where a hover/focus accent
     needs to show up against a navy background (the stat band items,
     the gallery's keyboard focus ring) — --color-navy itself would
     disappear there. The WhatsApp buttons used to live here too, but
     now use --color-navy directly (see .whatsapp-btn below) to match
     the rest of the page's main CTAs instead of standing apart in a
     lighter blue of their own. */
  --color-accent: #4272b0;
  --color-ink: #14181e;
  --color-white: #ffffff;
  --color-pale-blue: #eff5fb;
  --color-text-muted: #626a75;
  --color-line: #e1e3e7;

  --font-heading: "Playfair Display", Georgia, serif;
  --font-body: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --max-width: 1120px;
  /* Widened from 42em: at the section width this site actually uses,
     42em was leaving a very noticeable slab of empty space to the right
     of every description paragraph and wrapping lines well before the
     column actually ran out of room. */
  --measure: 60em;

  /* A small deliberate radius scale in place of one-off pixel values
     picked per component (an earlier pass through this file turned up
     13 different border-radius values for what was really only ever
     four or five distinct "roundnesses"). 999px (full pill) and 50%
     (perfect circle) are left as literals below since those are exact,
     self-explanatory shape constants rather than scale steps. */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-pill: 999px;

  /* Same idea for the small utility/caption text scattered through
     labels, hints, badges and buttons — this file had nine different
     sizes between 0.66rem and 0.88rem, mostly a few hundredths of a
     rem apart from each other with no real reason. Headings, the hero
     type and other deliberately bespoke display sizes are left alone;
     this scale only covers small supporting text. */
  --text-2xs: 0.68rem;
  --text-xs: 0.72rem;
  --text-sm: 0.78rem;
  --text-md: 0.85rem;
  --text-lg: 0.88rem;

  /* The warm gold used for the guest-review star rating — a near-universal
     convention for ratings (Google, Booking.com, Trustpilot all use it),
     so it reads as "how ratings look" rather than a second site accent.
     Scoped to that one use; nothing else on the page borrows it. */
  --color-gold: #f2a83c;
}

/* ==========================================================================
   Reset / base
   ========================================================================== */
* {
  box-sizing: border-box;
}

/* Matches the footer's colour so any overscroll/rubber-band bounce past
   the very bottom of the page shows this instead of a flash of plain
   white beneath the footer, and turns off the bounce itself (Safari 16+,
   Chrome, Firefox all honour this on the document element) so there is
   nothing to reveal past the page's real edges in the first place. */
html {
  background: var(--color-navy-deep);
  overscroll-behavior-y: none;
}

/* The logo links to "#apartman" as a plain anchor (unlike the tab nav
   links, which intercept their clicks and switch sections without ever
   touching the browser's own scroll position), so it's the one place a
   native browser jump-to-anchor actually happens. Without this it's an
   instant teleport; smooth-scrolling it reads as a deliberate "take me
   back to the top" motion instead. Gated behind prefers-reduced-motion
   like every other animation on the page. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-ink);
  /* Matches <html> and the footer/page-loader, rather than white: the
     actual white page background comes from .page-section below, which
     now paints its own opaque white on top of this. Without that, this
     navy would show through everywhere. Keeping body itself navy (not
     white) is what fixes the bottom Safari safe-area/overscroll strip —
     on iOS that strip samples body's background-color, not <html>'s, so
     <html> alone being navy wasn't enough. */
  background-color: var(--color-navy-deep);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
  /* A very faint dot grid gives the plain white panels some quiet
     texture instead of flat, empty white — subtle enough to sit behind
     body text without affecting readability. Only visible through the
     opaque .page-section panels above it, same as before. */
  background-image: radial-gradient(circle, rgba(0, 35, 73, 0.07) 1px, transparent 1px);
  background-size: 26px 26px;
}

/* A brief branded loader over the whole page while the hero image and
   fonts finish loading — js/main.js hides it on window's load event
   (with a safety timeout so it can never get stuck showing). Removed
   from the DOM entirely once hidden. */
#page-loader {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.1rem;
  background: var(--color-navy-deep);
  transition: opacity 0.4s ease;
}

#page-loader.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.page-loader__mark {
  width: 200px;
  height: auto;
  max-width: 60vw;
}

.page-loader__spinner {
  width: 2.1rem;
  height: 2.1rem;
  border: 3px solid rgba(255, 255, 255, 0.18);
  border-top-color: var(--color-white);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

h1 {
  font-family: var(--font-heading);
  font-weight: 600;
  color: var(--color-navy);
  letter-spacing: -0.01em;
  margin: 0 0 0.5em;
  font-size: clamp(2.4rem, 5vw, 3.8rem);
  line-height: 1.08;
  max-width: 16em;
}

/* Section headings repeat on every tab, so they stay in the body sans
   font (bolder, to still carry the weight of a heading) rather than
   switching to the serif each time — the serif is now reserved for the
   hero title and Nada's name, where it reads as a deliberate flourish
   instead of a second competing voice throughout the page. */
h2 {
  font-family: var(--font-body);
  font-weight: 700;
  color: var(--color-navy);
  letter-spacing: -0.01em;
  margin: 0 0 0.5em;
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  line-height: 1.2;
}

p {
  margin: 0 0 1.1em;
  max-width: var(--measure);
}

a {
  color: var(--color-navy);
}

.eyebrow {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--color-steel);
  margin: 0 0 0.85em;
}

/* ==========================================================================
   Header
   ========================================================================== */
/* Starts transparent over the hero photo (position: fixed takes it out
   of flow entirely, so the hero fills the space it would otherwise
   reserve) and fades to a solid bar once main.js adds .is-scrolled —
   the same navy the header always used to be, just arrived at instead
   of starting there. A thin gradient at the very top of the hero
   (.hero-overlay__nav-fade below) keeps the logo and buttons readable
   over a bright photo before that happens. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: transparent;
  color: var(--color-white);
  box-shadow: none;
  border-bottom: 1px solid transparent;
  transition: background-color 0.35s ease, box-shadow 0.35s ease, border-color 0.35s ease;
}

.site-header.is-scrolled {
  background: var(--color-navy);
  /* The hairline needed real contrast to read as an edge once the bar
     is a solid colour block sitting over ordinary page content — the
     original 0.08 alpha all but disappeared against a light section
     background further down the page. */
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.22);
  border-bottom-color: rgba(255, 255, 255, 0.22);
}

.main-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  /* Constrained to the same column as the rest of the page (sub-nav,
     hero bar, section content all already do this) instead of running
     edge-to-edge — on a wide screen the brand mark and WhatsApp button
     were sitting far past the content's own right edge below them.
     Vertical padding trimmed from 1.5rem to 1.1rem: at 95px tall the
     bar was noticeably deeper than it needed to be for its content. */
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1.1rem 2rem;
}

/* A small wordmark so the bar carries the site's own identity instead of
   being just a WhatsApp button and some flags on a flat colour block. */
.main-bar__brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  /* Explicit rather than relying on the browser's own auto-cursor-for-links
     default: some engines don't resolve that default to a pointer once the
     link itself is a flex container, and fall back to a text-selection
     cursor over the logo instead. */
  cursor: pointer;
}

/* Sized to the header's own height rather than a fixed rem value, so it
   keeps working if the header's padding ever changes again. */
.main-bar__brand-logo {
  height: 50px;
  width: auto;
  display: block;
}

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

.sub-nav {
  background: var(--color-white);
  border-bottom: 1px solid var(--color-line);
}

.sub-nav ul {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 2rem;
  display: flex;
  gap: 2.25rem;
  list-style: none;
}

.sub-nav a {
  display: inline-block;
  padding: 1.9rem 0 1.15rem;
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.sub-nav a:hover,
.sub-nav a.active {
  color: var(--color-navy);
  border-bottom-color: var(--color-navy);
}

/* Plain text codes (EN / IT / DE / PL / CZ / HR) instead of emoji
   flags: flags represent countries, not languages (English isn't the
   UK's alone), and they render inconsistently across operating systems
   — plain uppercase letters read as a deliberate editorial choice and
   look the same everywhere. */
.lang-switcher {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

.lang-switcher button {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 0.3rem 0.05rem;
  color: rgba(255, 255, 255, 0.55);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.lang-switcher button:hover,
.lang-switcher button.active {
  color: var(--color-white);
}

.lang-switcher button.active {
  border-bottom-color: var(--color-steel-light);
}

.whatsapp-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  overflow: hidden;
  text-decoration: none;
  font-size: var(--text-md);
  /* 600 rather than 700, and slightly tighter tracking — full-weight
     Poppins in uppercase read heavier and more generic than the rest
     of the page's type, closer to a coupon banner than a boutique
     site's button label. */
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 0.85rem 1.4rem;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  /* A neutral, dark-tinted shadow rather than a colored green glow — the
     glow read closer to a game HUD's "reward ready" button than a
     considered piece of a boutique hotel's site. */
  box-shadow: 0 4px 14px rgba(0, 24, 48, 0.22);
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease,
    transform 0.15s ease, box-shadow 0.15s ease;
}

/* A single soft light catching the button's surface on hover, rather than
   a colour change alone — restrained on purpose (white at low opacity,
   sweeping once, no repeat or glow) so it reads as a polished surface,
   not a game reward animation. Skipped for reduced-motion visitors. */
.whatsapp-btn::before {
  content: "";
  position: absolute;
  top: 0;
  left: -60%;
  width: 45%;
  height: 100%;
  background: linear-gradient(115deg, transparent, rgba(255, 255, 255, 0.32), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .whatsapp-btn::before {
    transition: left 0.65s ease;
  }

  .whatsapp-btn:hover::before {
    left: 130%;
  }
}

.whatsapp-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(0, 24, 48, 0.3);
}

.whatsapp-icon {
  width: 1.3em;
  height: 1.3em;
  flex-shrink: 0;
}

/* This one lives in the header, which is transparent over the hero
   photo at first (where the button already reads fine on its own —
   no edge needed) and only turns solid navy once scrolled
   (.site-header.is-scrolled) — the point where a navy-on-navy button
   would otherwise vanish. Rather than a hard border/"lining" (the
   look that didn't work out for the rating card earlier), a soft
   light glow appears at that point instead — a crisp hairline right
   at the edge plus a diffused halo just past it, so it reads as
   light catching the button rather than an outlined shape. Kept as
   box-shadow (not border) so it layers with the button's own drop
   shadow in one property, and transitions smoothly via the box-shadow
   entry already in .whatsapp-btn's own transition list. */
.whatsapp-btn--nav {
  background: var(--color-navy);
  color: var(--color-white);
  border: 1px solid transparent;
  font-size: var(--text-sm);
  padding: 0.75rem 1.25rem;
  /* Repeats .whatsapp-btn's own transition list plus opacity -- a plain
     `transition: opacity ...` here would replace that whole list rather
     than add to it, since this rule comes after it at equal specificity. */
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease,
    transform 0.15s ease, box-shadow 0.15s ease, opacity 0.25s ease;
}

/* Hidden only while the hero-cta bar's own WhatsApp button is still in
   view -- see the matching comment in main.js. Defaults to visible (no
   class needed) if JS never runs, rather than defaulting to hidden and
   depending on JS to reveal it. */
.site-header.hero-cta-in-view .whatsapp-btn--nav {
  opacity: 0;
  pointer-events: none;
}

.site-header.is-scrolled .whatsapp-btn--nav {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.28), 0 0 16px rgba(255, 255, 255, 0.12),
    0 4px 14px rgba(0, 24, 48, 0.22);
}

.whatsapp-btn--nav:hover {
  background: var(--color-steel);
  border-color: var(--color-steel);
}

.site-header.is-scrolled .whatsapp-btn--nav:hover {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.28), 0 0 16px rgba(255, 255, 255, 0.12),
    0 10px 22px rgba(0, 24, 48, 0.3);
}

/* Sits on the hero bar's own navy/navy-deep gradient, which is the
   same problem as the nav button above but permanent rather than
   scroll-triggered — this background never turns transparent, so the
   glow is always on rather than conditional. */
.whatsapp-btn--hero {
  background: var(--color-navy);
  color: var(--color-white);
  border: 1px solid transparent;
  padding: 0.7rem 1.6rem;
  font-size: var(--text-lg);
  line-height: 1.2;
  flex-shrink: 0;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.28), 0 0 16px rgba(255, 255, 255, 0.12),
    0 4px 14px rgba(0, 24, 48, 0.22);
}

.whatsapp-btn--hero:hover {
  background: var(--color-steel);
  border-color: var(--color-steel);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.28), 0 0 16px rgba(255, 255, 255, 0.12),
    0 10px 22px rgba(0, 24, 48, 0.3);
}

/* ==========================================================================
   Main / sections
   ========================================================================== */
main {
  max-width: var(--max-width);
  margin: 0 auto;
  /* No horizontal padding here on purpose: main's only children are
     .page-section elements, each of which already centres and pads
     its own .section-body independently. Padding on both main AND
     section-body was stacking (32px + 32px), pushing the rating card,
     gallery heading and contact fields 40px further right than the
     stat band's own numbers — which self-centre against the full
     viewport instead of against this already-padded main. Removing
     main's own padding leaves exactly one inset in effect, so
     everything lines up on the same left edge. */
  padding: 0;
}

.page-section {
  position: relative;
  overflow: hidden;
  /* The logo's href="#apartman" is a plain anchor link (unlike the
     nav-links, which intercept their own clicks and never actually
     scroll), so a native browser jump lands here directly. Without
     this, that jump puts this section's own top flush against the
     viewport's top edge — right where the fixed header already sits —
     scrolling the sub-nav tabs above it entirely out of view. The
     offset (fixed header height + sub-nav height, kept current by
     js/main.js) leaves exactly enough room for both to still be
     visible, landing on the same layout the page shows on a fresh
     load. The fallback covers the brief instant before that JS runs. */
  scroll-margin-top: var(--section-scroll-offset, 160px);
  padding: 0 0 4.5rem;
  /* Now that <body> itself is navy (see body's own rule, for the Safari
     safe-area fix), each section needs to paint its own opaque white —
     otherwise the whole page would read as navy, not just the safe
     area/overscroll edges. <main> (this element's parent) is boxed at
     max-width 1120px and centered, so without breaking out of that box
     below, this white would only ever cover that centre column, leaving
     <body>'s new navy showing as two bars down the left/right edges on
     any viewport wider than 1120px. width: 100vw + the negative margin
     is the standard "full-bleed from a centered container" trick — it
     stretches just the background/box to the true viewport edges while
     .section-body's own max-width + auto margins still keep the actual
     content centered exactly as before. */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  background-color: var(--color-white);
  /* A soft directional glow in one corner ties the navy brand colour
     into the plain white panels, on top of the dot-grid from <body>,
     so the background reads as designed rather than flat and empty. */
  background-image: radial-gradient(ellipse 55% 45% at 88% 0%, rgba(0, 35, 73, 0.055), transparent 70%);
}

.page-section + .page-section {
  border-top: 1px solid var(--color-line);
}

/* A couple of large, faint outlined rings per section — a quiet
   editorial accent (rather than a filled shape) that reads as
   intentional design rather than decoration for its own sake. They sit
   behind all content and never affect layout. Alternating sides on
   every other section keeps the rhythm from feeling like the same
   sticker pasted onto each panel. */
.page-section::before,
.page-section::after {
  content: "";
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  border: 1px solid rgba(0, 35, 73, 0.09);
  pointer-events: none;
}

.page-section::before {
  width: 26rem;
  height: 26rem;
  top: -9rem;
  right: -8rem;
}

.page-section::after {
  width: 16rem;
  height: 16rem;
  bottom: -6rem;
  left: -6rem;
  border-color: rgba(0, 35, 73, 0.06);
}

.page-section:nth-of-type(even)::before {
  right: auto;
  left: -8rem;
}

.page-section:nth-of-type(even)::after {
  left: auto;
  right: -6rem;
}

.page-section > .section-body {
  position: relative;
  z-index: 1;
}

.tagline {
  font-size: 1.1rem;
  color: var(--color-text-muted);
  max-width: var(--measure);
}

/* A full-bleed navy band commits to the brand colour as one bold
   statement instead of scattering it in small accents, the way
   Villa Diamond and Fleet Elevera anchor their key numbers on a solid
   colour block. It breaks out of the centred content column using the
   100vw/negative-margin trick; the parent .page-section already sets
   overflow: hidden, so this can't introduce a horizontal scrollbar. */
.stat-band {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  margin-top: 2rem;
  margin-bottom: 2.5rem;
  background: var(--color-navy);
}

.stat-band__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  /* 2rem horizontal, matching every other section's side inset (the
     stat numbers were sitting 8px further left than the rating card
     and gallery below them, since this band re-centres itself against
     the full viewport rather than nesting inside main). */
  padding: 2.25rem 2rem;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 1.75rem 1rem;
}

/* A quiet highlight on hover — the accent blue picks out one figure at
   a time instead of the whole band sitting flat, the same idea as a
   feature list highlighting the item under the cursor, adapted to a
   row instead of a vertical list (a top edge here rather than a left
   one, since these sit side by side). */
.stat-band__item {
  text-align: center;
  padding: 0.85rem 0.6rem;
  border-radius: var(--radius-md);
  border-top: 2px solid transparent;
  transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
}

.stat-band__item:hover {
  background: rgba(255, 255, 255, 0.05);
  border-top-color: var(--color-accent);
  transform: translateY(-2px);
}

.stat-band__value {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1.85rem;
  font-weight: 700;
  color: var(--color-white);
  white-space: nowrap;
}

.stat-band__label {
  margin: 0.4rem 0 0;
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-steel-light);
  transition: color 0.2s ease;
}

.stat-band__item:hover .stat-band__label {
  color: var(--color-white);
}

@media (max-width: 900px) {
  .stat-band__inner {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 480px) {
  .stat-band__inner {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem 0.75rem;
  }

  .stat-band__value {
    font-size: 1.5rem;
  }
}

/* Houses the rating card and the reviews badge side by side — two
   separate trust signals that read better as one row than as two
   stacked, unrelated lines. Wraps on narrow screens, where the pair
   goes back to stacking, each still centred on its own. */
.trust-row {
  display: flex;
  /* Stretch rather than center: the rating card's 3-line label makes it
     naturally taller than the badge's single line, so centering left
     them sitting at mismatched heights side by side. Stretching both to
     the row's height and letting each card's own centered content float
     in the middle of that shared height reads as a matched pair. On
     narrow screens each already gets width: 100% and wraps onto its own
     line, so stretch has nothing left to equalize there — the explicit
     min-height further down still does that job on mobile. */
  align-items: stretch;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1.25rem;
  margin: 1.5rem auto 1rem;
}

/* Booking.com trust badges — styled as small outlined pills rather than
   plain underlined links, consistent with the rest of the button language
   on the page. Centred and sized as a standalone landmark rather than a
   left-hugging inline link, since nothing else shares its line. */
.booking-badge {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7em;
  width: fit-content;
  margin: 0;
  padding: 1.15rem 2.5rem;
  background: var(--color-navy);
  border: 1px solid var(--color-navy);
  /* Matches .rating-card's radius -- the two sit side by side in
     .trust-row as a paired set of trust signals, so a different corner
     radius between them read as an inconsistency rather than a choice. */
  border-radius: var(--radius-md);
  /* Clips the hover sweep below to the button's own rounded corners
     instead of it spilling past them as a hard rectangle. */
  overflow: hidden;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #ffffff;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: 0 6px 16px rgba(0, 35, 73, 0.25);
  transition: background 0.25s ease, box-shadow 0.2s ease, transform 0.2s ease;
}

/* The same restrained "light catching the surface" treatment already
   used on the WhatsApp buttons (see .whatsapp-btn::before above),
   reused here instead of inventing a separate effect — a diagonal
   highlight sweeps once on hover while the background itself eases to
   a deeper navy, rather than a hard colour wipe. */
.booking-badge::before {
  content: "";
  position: absolute;
  top: 0;
  left: -60%;
  width: 45%;
  height: 100%;
  background: linear-gradient(115deg, transparent, rgba(255, 255, 255, 0.32), transparent);
  transform: skewX(-18deg);
  pointer-events: none;
  z-index: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .booking-badge::before {
    transition: left 0.65s ease;
  }

  .booking-badge:hover::before {
    left: 130%;
  }
}

/* Also matched as ".js-reveal .booking-badge.is-visible:hover" below:
   the scroll-reveal rules further down set this same `transform`
   property with 3 classes' worth of specificity once an element has
   been revealed (".js-reveal .booking-badge.is-visible"), which beats
   a plain ".booking-badge:hover" (2) outright — in practice the lift
   silently never showed once a visitor had scrolled to it, which is
   effectively always. The second selector matches that specificity so
   hover keeps working after the reveal has run. */
.booking-badge:hover,
.js-reveal .booking-badge.is-visible:hover {
  background: var(--color-navy-deep);
  box-shadow: 0 12px 26px rgba(0, 35, 73, 0.34);
  transform: translateY(-3px);
}

/* A single quotation mark rather than a star: a lone star read as a
   1-star rating at a glance, which is the opposite of the point. The
   quote mark instead echoes the same big faint " already used behind
   the guest quotes below, so this button reads as "reviews" without
   implying any specific score. Positioned above the sweep layer so it
   (and the label text below) stay visible as the colour travels
   across underneath. Steel-light rather than gold: the same muted-blue
   accent already used for labels on other navy surfaces (see
   .stat-band__label) instead of the star-rating gold, which read as
   too warm/attention-grabbing for a mark that's just decorative. */
.booking-badge__icon {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  font-family: var(--font-heading);
  font-size: 1.7rem;
  line-height: 0.6;
  color: var(--color-steel-light);
  transition: transform 0.3s ease, color 0.3s ease;
}

.booking-badge:hover .booking-badge__icon {
  transform: scale(1.2);
}

.booking-badge > span:last-child {
  position: relative;
  z-index: 1;
}

/* The rating card: a solid score chip with a small speech-bubble
   tail, paired with a two-line label (headline + source), so the
   trust signal reads with real weight instead of a plain text pill.
   Sits side by side with the Booking.com reviews badge in .trust-row
   below rather than centred on its own line. */
.rating-card {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  width: fit-content;
  margin: 0;
  padding: 1.1rem 2.6rem 1.1rem 1.1rem;
  background: var(--color-white);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  text-decoration: none;
  box-shadow: 0 8px 20px rgba(0, 35, 73, 0.1);
  transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}

/* See the matching comment on .booking-badge:hover above — the same
   specificity fix is needed here so the lift still applies once the
   card has been through its scroll-reveal entrance. */
.rating-card:hover,
.js-reveal .rating-card.is-visible:hover {
  box-shadow: 0 16px 32px rgba(0, 35, 73, 0.2);
  transform: translateY(-3px);
}

.rating-card__score {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  min-width: 2.6rem;
  height: 2.6rem;
  padding: 0 0.55rem;
  flex-shrink: 0;
  background: var(--color-navy);
  border-radius: 7px 7px 7px 2px;
  color: var(--color-white);
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 1.15rem;
}

/* "/10" spelled out in the chip itself — on its own, "10" reads as an
   arbitrary badge number rather than a rating. Small and muted so the
   score itself still leads. */
.rating-card__score-suffix {
  font-size: 0.62em;
  font-weight: 600;
  opacity: 0.7;
  margin-left: 0.05em;
}

/* A small tail peeking out under the score chip's near-sharp corner,
   like a speech bubble, so it reads as a badge rather than a plain
   rounded square. */
.rating-card__score::after {
  content: "";
  position: absolute;
  bottom: -0.3rem;
  left: 0.35rem;
  width: 0.65rem;
  height: 0.65rem;
  background: var(--color-navy);
  transform: rotate(45deg);
  border-radius: 0 0 0 2px;
}

.rating-card__text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

/* Steel rather than gold: the same blue-grey accent already used for
   small uppercase labels on light backgrounds elsewhere (see .eyebrow)
   — the mirror of steel-light's role on navy surfaces (see
   .booking-badge__icon) — instead of a gold that only ever meant
   "rating" here and nowhere else in the palette. */
.rating-card__stars {
  color: var(--color-steel);
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  line-height: 1;
}

.rating-card__star {
  display: inline-block;
}

/* A quick twinkle across the five stars on hover, one after another,
   rather than a border or outline appearing around the card — a small
   bit of delight tied to what the card actually is (a rating), instead
   of a generic "hovered" state applied to every trust badge alike. */
@media (prefers-reduced-motion: no-preference) {
  .rating-card:hover .rating-card__star {
    animation: star-twinkle 0.45s ease;
  }

  .rating-card:hover .rating-card__star:nth-child(1) { animation-delay: 0s; }
  .rating-card:hover .rating-card__star:nth-child(2) { animation-delay: 0.07s; }
  .rating-card:hover .rating-card__star:nth-child(3) { animation-delay: 0.14s; }
  .rating-card:hover .rating-card__star:nth-child(4) { animation-delay: 0.21s; }
  .rating-card:hover .rating-card__star:nth-child(5) { animation-delay: 0.28s; }
}

@keyframes star-twinkle {
  0% { transform: scale(1); }
  45% { transform: scale(1.45); }
  100% { transform: scale(1); }
}

.rating-card__headline {
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-navy);
}

.rating-card__source {
  font-size: var(--text-md);
  color: var(--color-text-muted);
}

/* Guest reviews: one large "spotlight" testimonial at a time rather than
   a row of small cards, with dot pagination beneath it — a calmer, more
   editorial presentation that also gives visitors a clear, clickable
   sense of how many reviews there are and where they are in the set. */
/* A visible break before this section starts, not just more margin —
   the map, the Booking.com badge and these guest quotes are three
   unrelated things that were reading as one continuous block with
   nothing marking where one ends and the next begins. */
.reviews.content-band {
  margin-top: 3rem;
  padding-top: 2.25rem;
  border-top: 1px solid var(--color-line);
}

.reviews__stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.75rem;
  margin-top: 1.5rem;
}

.reviews__carousel {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  width: 100%;
}

/* A fixed-height window: the outgoing and incoming slides are both
   absolutely positioned inside it and cross over each other, so the
   viewport's own height never changes and the page never jumps. */
/* js/main.js measures every review once (in the current language) and
   sets this to the tallest one, so the box has one stable height while
   flipping between reviews instead of visibly resizing each time. It's
   only re-measured on a language change or a resize (translated text
   can need more or less room), which is what the transition below is
   for — a smooth resize for those rarer moments, not for every slide. */
.reviews__viewport {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 10rem;
  overflow: hidden;
  transition: height 0.4s ease;
}

.review-spotlight {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.4s ease-out;
  will-change: transform, opacity;
}

.review-spotlight__inner {
  position: relative;
  max-width: 42em;
  margin: 0 auto;
  padding: 2.75rem 1.5rem 0;
  text-align: center;
  transition: transform 0.35s ease;
}

/* The whole quote gently grows on hover — a small invitation to read
   rather than a static block of text sitting in the page. Scale, not
   font-size, so nothing reflows or shifts underneath it. */
@media (prefers-reduced-motion: no-preference) {
  .review-spotlight__inner:hover {
    transform: scale(1.035);
  }
}

/* A large, faint serif quotation mark behind the copy — a small
   editorial flourish rather than a plain bordered box. Deepens
   slightly along with the zoom above. */
.review-spotlight__inner::before {
  content: "\201C";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--font-heading);
  font-size: 5.5rem;
  line-height: 1;
  color: rgba(0, 35, 73, 0.1);
  pointer-events: none;
  transition: color 0.35s ease;
}

.review-spotlight__inner:hover::before {
  color: rgba(0, 35, 73, 0.18);
}

.review-spotlight__stars {
  position: relative;
  margin: 0 0 1rem;
  color: var(--color-gold);
  font-size: 1rem;
  letter-spacing: 0.15em;
}

.review-spotlight__quote {
  position: relative;
  margin: 0 0 1.25rem;
  font-size: 1.28rem;
  line-height: 1.55;
  font-weight: 500;
  color: var(--color-ink);
}

.review-spotlight__author {
  position: relative;
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.reviews__arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 3.1rem;
  height: 3.1rem;
  align-self: center;
  border: 1px solid var(--color-line);
  border-radius: 50%;
  background: var(--color-white);
  color: var(--color-navy);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease,
    transform 0.15s ease, box-shadow 0.2s ease;
}

.reviews__arrow:hover {
  background: var(--color-navy);
  border-color: var(--color-navy);
  color: var(--color-white);
  transform: translateY(-2px);
  box-shadow: 0 8px 18px rgba(0, 35, 73, 0.22);
}

.reviews__arrow:active {
  transform: translateY(0);
}

.reviews__arrow:disabled {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
}

/* Pagination dots beneath the testimonial: a real, clickable way to see
   how many reviews there are and jump straight to one, styled as small
   navy rings that fill solid and stretch into a pill when active — bold
   and legible rather than the barely-there dots this replaces. */
.reviews__dots {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.reviews__dot {
  width: 0.7rem;
  height: 0.7rem;
  padding: 0;
  border: 2px solid rgba(0, 35, 73, 0.45);
  border-radius: var(--radius-pill);
  background: rgba(0, 35, 73, 0.12);
  cursor: pointer;
  transition: background 0.25s ease, border-color 0.25s ease, width 0.25s ease,
    transform 0.15s ease;
}

.reviews__dot:hover {
  border-color: var(--color-navy);
  transform: scale(1.15);
}

.reviews__dot.active {
  width: 1.7rem;
  border-color: var(--color-navy);
  background: var(--color-navy);
}

/* A rounded pale-blue "surface" band that separates a block of
   content (the gallery, the reviews) from the plain white sections
   around it — without it, everything just reads as one long white
   page with no sense of moving between distinct zones. A soft navy
   glow tucked into one corner, plus a quiet dot-grid, gives the band
   some depth without ever reading as a dark or heavy patch — a light,
   uniform blue underneath it stays the dominant colour throughout. */
.content-band {
  background-color: var(--color-pale-blue);
  background-image: radial-gradient(ellipse 70% 60% at 10% 0%, rgba(0, 35, 73, 0.08), transparent 70%),
    radial-gradient(circle, rgba(0, 35, 73, 0.07) 1px, transparent 1px);
  background-size: auto, 26px 26px;
  border-radius: var(--radius-lg);
  padding: 2.25rem 2rem;
  margin: 2.5rem 0;
  /* A soft lift off the white page — the same card language as the
     rating badge, booking badge and map embed below, so this band reads
     as its own distinct surface instead of just a colour change. */
  box-shadow: 0 1px 2px rgba(0, 35, 73, 0.05), 0 18px 34px -18px rgba(0, 35, 73, 0.28);
}

.content-band > .eyebrow:first-child {
  margin-top: 0;
}

/* An asymmetric layout instead of a uniform grid of same-size tiles —
   the first photo is featured large (a 2x2 block), the next four fill
   the space beside it, and the last two share the final row. Real
   photos read as a curated set this way instead of a flat wall of
   equal squares. grid-auto-flow: dense lets those last two (each
   spanning 2 columns) fill the row completely with no gaps. */
.gallery {
  margin-top: 1.5rem;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 165px;
  grid-auto-flow: dense;
  gap: 0.6rem;
}

.gallery__item:first-child {
  grid-column: span 2;
  grid-row: span 2;
}

.gallery__item:nth-child(6),
.gallery__item:nth-child(7) {
  grid-column: span 2;
}

.gallery__item {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: none;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: none;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(0, 35, 73, 0.14);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.gallery__item:hover,
.gallery__item:focus-visible {
  transform: translateY(-4px) scale(1.015);
  box-shadow: 0 14px 26px rgba(0, 35, 73, 0.2);
}

.gallery__item:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.gallery img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* A dim overlay with an expand icon — hidden until hover/focus, so the
   grid reads as calm photos rather than a wall of visible buttons, but
   still signals clearly that each one opens larger. */
.gallery__zoom {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 20, 40, 0.32);
  color: #ffffff;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.gallery__item:hover .gallery__zoom,
.gallery__item:focus-visible .gallery__zoom {
  opacity: 1;
}

/* Dot pagination for the mobile photo carousel below — irrelevant on
   wider screens where .gallery is still a plain grid, so hidden there. */
.gallery-dots {
  display: none;
}

/* "N / total" counter badge, shown only on the mobile carousel (see the
   640px media query) in place of the hover-only zoom hint. */
.gallery__count {
  display: none;
}

/* A transitional zone just above the mobile breakpoint where the
   section is still narrow enough that the full-size spotlight quote
   needs a couple of extra lines — without this, a tablet or a narrow
   browser window could clip the longest reviews. */
@media (min-width: 641px) and (max-width: 900px) {
  .review-spotlight__inner {
    padding: 2.5rem 1rem 0;
  }

  .review-spotlight__quote {
    font-size: 1.12rem;
  }
}

@media (max-width: 640px) {
  /* The photo band still breaks out to the full width of the screen — the
     same 100vw/negative-margin trick .stat-band uses higher up the page —
     so the carousel reads as big and immersive rather than a small inset
     card. The eyebrow/heading get their own padding back below so the
     text still lines up with the rest of the page. The photos themselves
     no longer go flush to the screen edges, though: each one is an inset,
     rounded, shadowed card with a sliver of its neighbour peeking in on
     either side, which both looks like an actual photo (not a flat wall
     of pixels) and signals at a glance that it's swipeable. */
  .content-band--photos {
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    padding-left: 0;
    padding-right: 0;
    border-radius: 0;
  }

  .content-band--photos > .eyebrow,
  .content-band--photos > h2 {
    /* Matches .section-body's own side padding so this heading lines up
       with every other heading on the page instead of sitting closer
       to the screen edge than the rest of the content. */
    padding: 0 2rem;
  }

  .gallery {
    display: flex;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    gap: 0.85rem;
    padding: 0 1.5rem;
    scroll-padding: 0 1.5rem;
    scrollbar-width: none;
  }

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

  .gallery__item {
    flex: 0 0 84%;
    height: 250px;
    scroll-snap-align: center;
    border-radius: var(--radius-xl);
    box-shadow: 0 10px 26px rgba(0, 35, 73, 0.28);
  }

  /* No hover state on touch, and the zoom hint would otherwise just
     sit permanently dim on whichever photo was last tapped. */
  .gallery__item:hover,
  .gallery__item:focus-visible {
    transform: none;
  }

  .gallery__zoom {
    display: none;
  }

  /* A quiet "N / total" counter in the corner of each card stands in for
     the hover zoom-hint (hidden on touch) and gives the carousel the same
     sense of a curated set that the desktop grid has, instead of just a
     bare photo. */
  .gallery__count {
    display: block;
    position: absolute;
    right: 0.6rem;
    bottom: 0.6rem;
    padding: 0.25rem 0.6rem;
    border-radius: var(--radius-pill);
    background: rgba(0, 20, 40, 0.55);
    color: #ffffff;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1;
    backdrop-filter: blur(2px);
    pointer-events: none;
  }

  .gallery-dots {
    display: flex;
    justify-content: center;
    gap: 0.4rem;
    margin-top: 0.9rem;
  }

  .gallery-dots__dot {
    width: 0.5rem;
    height: 0.5rem;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--color-line);
    cursor: pointer;
    transition: background-color 0.2s ease, width 0.2s ease;
  }

  .gallery-dots__dot.active {
    width: 1.4rem;
    border-radius: var(--radius-pill);
    background: var(--color-navy);
  }

  .reviews__carousel {
    flex-wrap: wrap;
    justify-content: center;
  }

  .reviews__viewport {
    order: 1;
    flex-basis: 100%;
  }

  .review-spotlight__inner {
    padding: 1.4rem 0.5rem 0;
  }

  .review-spotlight__inner::before {
    font-size: 2.75rem;
  }

  .review-spotlight__quote {
    font-size: 1.05rem;
  }

  .reviews__arrow {
    order: 2;
  }

  .booking-badge {
    /* Side-by-side (icon left, text right) reads fine on desktop where
       the label is one line, but once the label wraps to two lines here
       it put a lopsided shape on the button: the quote mark anchored to
       one side, two left-aligned lines of text hugging it on the other,
       and a lot of dead space wherever the shorter second line ended.
       Stacking the mark above the label instead — both centered as a
       column — reads as a deliberate pull-quote treatment and stays
       symmetrical regardless of how the text wraps. */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    text-align: center;
    white-space: normal;
    font-size: var(--text-lg);
    padding: 1.1rem 1.4rem;
  }

  .booking-badge__icon {
    /* No longer riding the first text line's baseline now that it sits
       on its own row above the label, so the earlier optical nudge for
       that side-by-side alignment no longer applies. */
    margin-top: 0;
  }
}

/* ==========================================================================
   Photo lightbox — a single full-screen viewer reused for every gallery
   photo, built once by js/main.js and toggled with .is-open rather than
   living in the page markup for each image.
   ========================================================================== */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 4rem 5rem;
  background: rgba(0, 12, 24, 0.92);
}

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

.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--radius-md);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}

.lightbox__close,
.lightbox__arrow {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s ease;
}

.lightbox__close:hover,
.lightbox__arrow:hover {
  background: rgba(255, 255, 255, 0.26);
}

.lightbox__close {
  top: 1.25rem;
  right: 1.5rem;
  width: 2.6rem;
  height: 2.6rem;
  font-size: 1.6rem;
}

.lightbox__arrow {
  top: 50%;
  transform: translateY(-50%);
  width: 3.1rem;
  height: 3.1rem;
  font-size: 1.7rem;
}

.lightbox__arrow--prev {
  left: 1.25rem;
}

.lightbox__arrow--next {
  right: 1.25rem;
}

@media (max-width: 640px) {
  .lightbox {
    padding: 4.5rem 0.75rem;
  }

  .lightbox__close {
    top: 0.75rem;
    right: 0.75rem;
  }

  .lightbox__arrow {
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1.4rem;
  }

  .lightbox__arrow--prev {
    left: 0.5rem;
  }

  .lightbox__arrow--next {
    right: 0.5rem;
  }
}

/* The photo gallery above and this "Surroundings" text are different
   sub-sections too — the same divider treatment marks that boundary,
   mirroring the one just below the map and the one above the reviews. */
.location-intro {
  margin-top: 3rem;
  padding-top: 2.25rem;
  border-top: 1px solid var(--color-line);
}

/* The map is its own sub-section, distinct from both the surroundings
   text above it and the Booking.com trust badges below it — the
   divider sits after the card (not before it) so it reads as closing
   off "the map" specifically, right before the badges start. */
.map-section {
  margin-top: 3rem;
  padding-bottom: 2.25rem;
  margin-bottom: 2rem;
  border-bottom: 1px solid var(--color-line);
}

/* Map embed — given the same elevated-card treatment as the hero bar,
   so it reads as a designed element rather than a raw dropped-in widget.
   The hover zoom applies to the whole card (address header included),
   not just the map itself, so hovering anywhere on it — not only the
   map area — reads as one block getting a closer look. */
.map-embed {
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: 0 12px 30px rgba(11, 22, 38, 0.12);
}

@media (prefers-reduced-motion: no-preference) {
  .map-embed {
    transition: transform 0.4s ease;
  }

  /* See the matching comment on .booking-badge:hover further up — the
     scroll-reveal rules set this same `transform` property with higher
     specificity once .map-embed has been revealed, which would
     otherwise silently cancel this hover scale for every visitor who's
     actually scrolled to it. */
  .map-embed:hover,
  .js-reveal .map-embed.is-visible:hover {
    transform: scale(1.02);
  }
}

.map-embed__frame {
  position: relative;
}

.map-embed iframe {
  display: block;
  width: 100%;
}

/* A branded skeleton sits over the map until the iframe actually fires
   its load event — the iframe is loading="lazy" so this only appears
   once a visitor scrolls near it, then fades out as soon as the map is
   really ready instead of the visitor staring at a blank grey rectangle. */
.map-embed__loading {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  background: var(--color-pale-blue);
  background-image: radial-gradient(circle, rgba(0, 35, 73, 0.07) 1px, transparent 1px);
  background-size: 26px 26px;
  transition: opacity 0.3s ease;
}

.map-embed__loading.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.map-embed__spinner {
  width: 2rem;
  height: 2rem;
  border: 3px solid rgba(0, 35, 73, 0.15);
  border-top-color: var(--color-navy);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

.map-embed__loading-text {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--color-text-muted);
}

/* A branded header bar sits above the map (not on top of it) — Google's
   own embed already shows its own address/directions card inside the map
   itself, so overlaying a second one there just duplicated it. This one
   is always visible, on any device, and never collides with Google's UI. */
.map-embed__header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 1.25rem;
  background: var(--color-white);
  border-bottom: 1px solid var(--color-line);
  text-decoration: none;
  transition: background-color 0.2s ease;
}

.map-embed__header:hover {
  background-color: var(--color-pale-blue);
}

.map-embed__pin {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--color-navy);
}

.map-embed__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.map-embed__title {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-navy);
}

.map-embed__subtitle {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer {
  position: relative;
  overflow: hidden;
  background: var(--color-navy-deep);
  color: rgba(255, 255, 255, 0.6);
  text-align: center;
  padding: 2.25rem 1.5rem;
  /* On an iPhone with a home-indicator, the page isn't allowed to paint
     into that reserved strip at all unless the viewport meta tag opts in
     (viewport-fit=cover, set in index.html) — without it, that sliver at
     the very bottom is left to the browser, which is the plain-white gap
     some visitors see below the footer. env() resolves to 0 anywhere that
     doesn't have an inset, so this is a no-op elsewhere. */
  padding-bottom: calc(2.25rem + env(safe-area-inset-bottom, 0px));
  font-size: var(--text-sm);
  letter-spacing: 0.02em;
}

/* A giant, near-invisible wordmark behind the footer text — the quiet
   "brand watermark" flourish nulladies.hr and Fleet Elevera use to give
   a plain footer some presence. Falls back to a very faint solid fill
   for browsers without text-stroke support, so it never disappears. */
.site-footer__watermark {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(4rem, 20vw, 10rem);
  letter-spacing: 0.05em;
  color: rgba(255, 255, 255, 0.045);
  white-space: nowrap;
  pointer-events: none;
  z-index: 0;
}

@supports (-webkit-text-stroke: 1px black) {
  .site-footer__watermark {
    color: transparent;
    -webkit-text-stroke: 1px rgba(255, 255, 255, 0.07);
  }
}

.site-footer__content {
  position: relative;
  z-index: 1;
  max-width: none;
}

.site-footer p {
  text-align: center;
  width: 100%;
  /* The global `p { max-width: 42em }` rule below is relative to each
     paragraph's own font-size, not the root size, so it was capping
     these lines at well under the footer's full width and leaving them
     stuck flush-left instead of letting them span (and center within)
     the full footer width. */
  max-width: none;
}

.site-footer__address {
  margin: 0;
  color: rgba(255, 255, 255, 0.85);
  font-weight: 500;
}

.site-footer__links {
  margin: 0.6rem 0 0;
}

.site-footer__links a {
  color: rgba(255, 255, 255, 0.8);
  text-decoration: none;
  transition: color 0.2s ease;
}

.site-footer__links a:hover {
  color: var(--color-white);
  text-decoration: underline;
}

.site-footer__dot {
  margin: 0 0.7em;
  color: rgba(255, 255, 255, 0.55);
  font-size: 1.15em;
}

.site-footer__copyright {
  margin: 1.4rem 0 0;
  color: rgba(255, 255, 255, 0.45);
  font-size: var(--text-xs);
}

/* The privacy-policy trigger sits inline with the phone/WhatsApp/Booking.com
   links above, so it needs to shed all of its default <button> chrome and
   pick up the exact same look — same font, same rest/hover colour, same
   underline-on-hover — rather than reading as a separate, oddly-styled
   control in that row. */
.site-footer__link-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.8);
  text-decoration: none;
  transition: color 0.2s ease;
}

.site-footer__link-btn:hover,
.site-footer__link-btn:focus-visible {
  color: var(--color-white);
  text-decoration: underline;
}

/* ==========================================================================
   Privacy-policy dialog
   ========================================================================== */
/* A native <dialog>, opened with showModal() from the footer link — it
   comes with focus-trapping and Escape-to-close for free, so the styling
   below only has to handle the look, plus a scrollable body once the
   policy text runs taller than the viewport. */
.privacy-dialog {
  width: min(560px, 92vw);
  max-height: min(80vh, 640px);
  padding: 0;
  border: none;
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px rgba(0, 24, 48, 0.35);
}

.privacy-dialog::backdrop {
  background: rgba(0, 20, 40, 0.55);
}

.privacy-dialog__inner {
  position: relative;
  padding: 2.25rem;
  max-height: min(80vh, 640px);
  overflow-y: auto;
}

.privacy-dialog__close {
  position: absolute;
  top: 0.9rem;
  right: 0.9rem;
  width: 2rem;
  height: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-pale-blue);
  color: var(--color-navy);
  border: none;
  border-radius: 50%;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s ease;
}

.privacy-dialog__close:hover,
.privacy-dialog__close:focus-visible {
  background: var(--color-line);
}

.privacy-dialog h2 {
  margin: 0 0 0.3em;
  padding-right: 2rem;
}

.privacy-dialog__meta {
  margin: 0 0 0.2em;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.privacy-dialog__note {
  margin: 0 0 1.4em;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-style: italic;
}

.privacy-dialog__body h3 {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-navy);
  margin: 1.5em 0 0.4em;
}

.privacy-dialog__body h3:first-child {
  margin-top: 0;
}

.privacy-dialog__body p {
  color: var(--color-ink);
  font-size: var(--text-md);
  line-height: 1.6;
  max-width: none;
}

@media (max-width: 480px) {
  .privacy-dialog {
    width: 94vw;
  }

  .privacy-dialog__inner {
    padding: 1.75rem 1.25rem;
  }
}

/* ==========================================================================
   Contact form
   ========================================================================== */
/* This is a static site with no backend, so the form below (see
   js/main.js) hands off to a wa.me link exactly like the WhatsApp
   buttons elsewhere on the page — it's just a friendlier way in for
   a visitor who'd rather fill in fields than open WhatsApp directly. */
.contact-section {
  border-top: 1px solid var(--color-line);
}

.contact-section .section-body {
  max-width: 40rem;
}

.contact-form {
  margin-top: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.contact-form__row {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.contact-form__row label,
.contact-form__group-label {
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--color-navy);
}

/* The check-in/check-out pair sits side by side under one shared group
   label — two small fields, so a two-column row reads more naturally
   than stacking them full-width like the other rows. */
.contact-form__dates {
  display: flex;
  gap: 0.75rem;
}

/* Two native date inputs side by side get cramped on a phone-width
   screen — some browsers give <input type="date"> a fairly wide,
   non-negotiable internal layout for its day/month/year segments, which
   can end up overpowering the flex-shrink here and leave the pair
   crowding each other and the row's right edge. Stacking them removes
   the width fight entirely: each field gets the full row width, with
   the same gap now running between them vertically instead. */
@media (max-width: 480px) {
  .contact-form__dates {
    flex-direction: column;
  }
}

.contact-form__date-field {
  flex: 1 1 0%;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-width: 0;
  /* Belt-and-suspenders against native <input type="date"> rendering a
     hair wider than the box its CSS width resolves to — some browsers'
     built-in calendar-icon layout isn't perfectly contained by
     width: 100%/border-box, which is what was pushing this field
     further right than the phone input above it despite both having
     identical CSS widths. Clipping here guarantees this field can never
     read wider than that box, whatever the cause. */
  overflow: hidden;
}

.contact-form__date-field label {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-text-muted);
}

.contact-form__date-field input {
  width: 100%;
  min-width: 0;
}

/* Also trims the picker icon's own default inline margin — the other
   commonly-cited source of this same overflow, in both Safari and
   Chrome (Chrome still uses the -webkit- pseudo-element name here). */
.contact-form__date-field input::-webkit-calendar-picker-indicator {
  margin-inline-start: 0;
}

.contact-form__row input,
.contact-form__row textarea {
  font: inherit;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  background: var(--color-white);
  color: var(--color-ink);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.contact-form__row textarea {
  resize: vertical;
  min-height: 6rem;
}

/* Clarifies that the phone field is only for an optional callback — the
   message itself always reaches WhatsApp either way, with or without it. */
.contact-form__hint {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.contact-form__row input:focus,
.contact-form__row textarea:focus {
  outline: none;
  border-color: var(--color-navy);
  box-shadow: 0 0 0 3px rgba(0, 35, 73, 0.12);
}

.contact-form__submit {
  align-self: flex-start;
  margin-top: 0.25rem;
  background: var(--color-navy);
  color: var(--color-white);
  border: 1px solid var(--color-navy);
}

.contact-form__submit:hover {
  background: var(--color-steel);
  border-color: var(--color-steel);
}

/* ==========================================================================
   Scroll reveal
   ========================================================================== */
/* html.js-reveal is added synchronously in <head>, before any of this
   content has even been parsed, so these start hidden from first
   paint — js/main.js then adds .is-visible to each one (via
   IntersectionObserver) the first time it scrolls into view, or
   removes js-reveal entirely if it can't run the effect properly, so
   nothing is ever left permanently invisible. */
/* Scoped to #apartman (see the matching comment in js/main.js) — the
   Host tab's own heading/paragraphs/profile card are intentionally left
   out of this hidden starting state so they're never at the mercy of a
   scroll-triggered reveal that has no reason to run on a short tab. */
.js-reveal #apartman .section-body > p,
.js-reveal #apartman .section-body > h2,
.js-reveal .stat-band,
.js-reveal .rating-card,
.js-reveal .booking-badge,
.js-reveal .content-band,
.js-reveal .location-intro,
.js-reveal .map-embed,
.js-reveal .contact-form {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

/* Matches the exact selector list above, each with .is-visible appended,
   so every rule here has the same specificity as its hidden-state
   counterpart and reliably wins the cascade on source order alone —
   a bare ".js-reveal .is-visible" rule is less specific than
   ".js-reveal .section-body > p" and would silently lose to it,
   leaving that paragraph or heading stuck invisible. */
.js-reveal #apartman .section-body > p.is-visible,
.js-reveal #apartman .section-body > h2.is-visible,
.js-reveal .stat-band.is-visible,
.js-reveal .rating-card.is-visible,
.js-reveal .booking-badge.is-visible,
.js-reveal .content-band.is-visible,
.js-reveal .location-intro.is-visible,
.js-reveal .map-embed.is-visible,
.js-reveal .contact-form.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 640px) {
  .site-header {
    flex-direction: column;
    align-items: flex-start;
  }

  .page-section {
    padding: 0 0 3rem;
  }

  .rating-card {
    gap: 0.85rem;
    padding: 0.8rem 1.2rem 0.8rem 0.8rem;
  }

  .contact-section .section-body {
    max-width: none;
  }

  /* Side by side on desktop, the rating card and the reviews badge are
     naturally different shapes (a compact score chip vs. a longer,
     wrapping line of text) — fine when they're reading as two distinct
     pills next to each other, but stacked on mobile that same mismatch
     reads as one card being oddly bigger than the other right above/
     below it. Matching them to the same width and a shared minimum
     height (tall enough for the badge's two-line label) makes the pair
     read as a matched set instead. */
  .rating-card,
  .booking-badge {
    width: 100%;
    min-height: 4.6rem;
  }
}


/* ==========================================================================
   Hero (full-bleed media band with overlaid content + floating CTA bar)
   ========================================================================== */
.hero-section {
  padding: 0;
}

.hero {
  position: relative;
  /* min-height: 68vh alone made the box's own shape wider (shorter,
     relative to its width) than the photo's native 1024×461 ratio on
     most desktop viewports — 68% of a ~900px-tall window is only
     ~610px, well short of the ~900px the photo would need at that
     width to show uncropped, so object-fit: cover below had to shave
     the top and bottom, cutting the roofline off. Pinning aspect-ratio
     to the photo's own ratio makes the box at least that tall for any
     given width, so cover never has anything left to crop away — the
     min-height only still matters as a floor on narrow/tall mobile
     viewports, where the aspect-ratio value alone would make the band
     too short and it falls back to cropping the sides instead, same as
     before. */
  aspect-ratio: 1024 / 461;
  min-height: 68vh;
  /* Explicit rather than the block default's implicit 100%: with width
     left as "auto", min-height overriding the aspect-ratio-derived
     height on narrow/tall (mobile) viewports left the browser treating
     that new, taller height as the definite axis and re-deriving width
     from IT via the same ratio instead — blowing the box out to ~1030px
     wide inside a 390px parent. Pinning width explicitly keeps it the
     one definite axis aspect-ratio is allowed to size height from. */
  width: 100%;
  display: flex;
  align-items: flex-end;
  color: var(--color-white);
  overflow: hidden;
}

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

.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.hero-overlay {
  position: absolute;
  inset: 0;
  /* Two layers: the original bottom-heavy fade that keeps the headline
     and CTA bar readable, plus a short fade at the very top so the now-
     transparent header's logo and buttons stay legible over a bright
     photo before .is-scrolled gives the bar its own navy background. */
  background:
    linear-gradient(to bottom, rgba(4, 8, 14, 0.5) 0%, rgba(4, 8, 14, 0) 160px),
    linear-gradient(to top, rgba(4, 8, 14, 0.9) 0%, rgba(4, 8, 14, 0.4) 55%, rgba(4, 8, 14, 0.08) 100%);
}

.hero-content {
  position: relative;
  z-index: 1;
  padding: 0 2rem 3rem;
  max-width: var(--max-width);
  margin: 0 auto;
  width: 100%;
}

.eyebrow--light {
  color: var(--color-steel-light);
}

.tagline--light {
  color: rgba(255, 255, 255, 0.85);
}

.hero-title {
  color: var(--color-white);
}

/* Hidden by default (desktop/tablet never show it — the hero-bar there
   is only ever as tall as its own content, so there's nothing below
   the button to scroll past before the tabs). Shown and positioned in
   the max-width: 640px block further down, where the mobile hero is
   stretched to fill the screen. */
.hero-scroll-cue {
  display: none;
}

/* Entrance animation: the hero title, tagline and the direct-booking bar
   rise and fade in on first load, staggered slightly so the page feels
   composed rather than everything just being there at once. Skipped
   entirely for anyone who's asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .hero-title,
  .hero-content .tagline,
  .hero-bar__inner {
    opacity: 0;
    animation: hero-rise 0.85s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  }

  .hero-title {
    animation-delay: 0.1s;
  }

  .hero-content .tagline {
    animation-delay: 0.3s;
  }

  .hero-bar__inner {
    animation-delay: 0.45s;
  }
}

@keyframes hero-rise {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes hero-scroll-cue-bounce {
  0%, 100% {
    transform: translateX(-50%) translateY(0);
  }
  50% {
    transform: translateX(-50%) translateY(7px);
  }
}

/* Shared by the map and page loading spinners below. */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* The hero bar is a single, focused direct-booking callout right under
   the photo — eyebrow, headline, note and the WhatsApp button, with a
   left accent bar for emphasis. The spec list that used to sit beside it
   was dropped: it just repeated the stat-band further down the page and
   made this band feel both redundant and too tall. It's a plain solid
   band directly under the photo (no floating/overlap trick), so it can
   never get cropped or wrap out of view regardless of viewport height. */
.hero-bar {
  background-color: var(--color-navy-deep);
  /* A quiet tonal shift, navy to navy — no colored glow. An earlier
     version bled a green radial-gradient in from the left to "lead the
     eye" toward the WhatsApp button, but paired with the badge's own
     glassy outline below, it read closer to a game HUD's glowing status
     bar than a boutique hotel's booking callout. */
  background-image: linear-gradient(100deg, var(--color-navy-deep) 0%, var(--color-navy) 100%);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.hero-bar__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1rem 2rem;
}

/* One row on desktop/tablet — the message card and the WhatsApp button
   side by side — wrapping only when the viewport is genuinely too
   narrow for that (the button drops to its own full-width line; see
   the max-width: 640px rules below). */
.hero-cta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.9rem 1.25rem;
}

/* A frosted-glass pane rather than a solid white card: solid white gave
   the discount message the strongest possible pop against the navy bar,
   but that was also the complaint -- it read as louder than everything
   else on the page. Dropping the fill to a faint white wash (8%, not the
   more obvious-looking ~14% first tried) plus a blur lets the bar's own
   navy gradient show through, so the card recedes into the bar instead
   of floating on top of it (hence no lift/box-shadow any more either --
   a shadow reads as "raised surface", which fights the recessed-glass
   look). -webkit-backdrop-filter covers Safari; a browser with neither
   just shows the faint navy-on-navy tint with no blur, which still
   reads fine. Every colour used inside this pane (white, steel-light)
   is one already used for text on navy elsewhere on the page (see
   .stat-band__label), so this isn't a new treatment invented just for
   this card -- it's the same "light text on navy" pairing used
   throughout, just applied to a translucent surface instead of a solid
   white one. 8% (not less) is also the floor for contrast, not just
   taste: any fainter and .hero-cta__badge's small uppercase label drops
   below AA's 4.5:1 minimum for small text. */
.hero-cta__card {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  flex: 1 1 0%;
  min-width: 14em;
  background: rgba(255, 255, 255, 0.08);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: var(--radius-md);
  padding: 0.85rem 1.4rem;
}

/* A plain tracked label — the same quiet "eyebrow" language used for
   every other small caption on the page (Location, Guest Reviews, Meet
   Your Host) — rather than its own separate component. The earlier
   version was a glassy outlined pill with a mint-green icon, which read
   less like a hotel's own understated tag and more like a status badge
   lifted from a game's HUD (that was about the badge's own shape/icon,
   not this card's translucency, so it doesn't rule out the frosted-glass
   card treatment above). Steel-light rather than the plain --color-steel
   used when this sat on white — same "light label on a navy surface"
   pairing already used for .stat-band__label just below on the page. */
.hero-cta__badge {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  font-size: var(--text-2xs);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--color-steel-light);
  white-space: nowrap;
}

.hero-cta__headline {
  margin: 0;
  /* flex-basis: 0 (not auto) — the badge already has a fixed width, so
     the headline should be sized purely from the remaining space, not
     from its own natural (unwrapped) text width. */
  flex: 1 1 0%;
  min-width: 12em;
  font-family: var(--font-heading);
  font-size: clamp(1.02rem, 1.6vw, 1.22rem);
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-white);
}

/* The actual money-saving claim — "cheaper than Booking.com" — used to
   be set in the same plain colour as its lead-in clause, so despite
   being the whole point of the bar, it read as flat as everything
   around it. Steel-light gives it its own identity here (accent blue,
   the choice from when this card was solid white, drops to a ~2:1
   contrast ratio against the frosted navy background -- nowhere near
   legible -- so it moved to the same light-on-navy blue used for
   .hero-cta__badge above and .stat-band__label elsewhere, which stays
   above 4.5:1 even at this card's faintest, worst-case background). */
.hero-cta__highlight {
  color: var(--color-steel-light);
  font-weight: 700;
}

.hero-bar .whatsapp-btn--hero {
  flex-shrink: 0;
  margin-left: auto;
}

.section-body {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 3.5rem 2rem 0;
}

/* Host tab: a profile card pairing Nada's photo with her name, a short
   personal greeting and the languages she speaks — the "hosted by a real
   person" framing that makes direct contact feel natural, sitting above
   the more practical property/location copy. */
.host-profile {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 1.1rem;
  margin: 0.5rem 0 2.5rem;
}

.host-profile__photo {
  flex: 0 0 auto;
}

.host-profile__photo img {
  display: block;
  width: 10rem;
  height: 10rem;
  border-radius: 50%;
  object-fit: cover;
  object-position: center 30%;
  border: 3px solid var(--color-white);
  box-shadow: 0 8px 24px rgba(0, 35, 73, 0.18);
}

/* Airport Transfer section: a route-map screenshot (Zadar Airport to
   Novalja) gives guests an at-a-glance sense of the drive before the copy
   explains timing and how to arrange it. The two sources are art-directed
   rather than just scaled -- the desktop crop is a wide 16:9 banner, the
   mobile crop is a tighter 3:2 so the route and its labels stay legible
   at phone width instead of shrinking to a sliver. */
.transfer-map {
  display: block;
  width: 100%;
  /* 1.5rem above, matching the gap every other section uses between its
     own heading and the first content block below it (.gallery,
     .reviews__stage) -- this one had been left at a much tighter 0.5rem,
     which read as cramped/rushed next to a big rectangular photo instead
     of a couple of lines of text, where the same small gap is barely
     noticeable. */
  margin: 1.5rem 0 2rem;
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: 0 6px 16px rgba(0, 35, 73, 0.14);
}

.transfer-map img {
  display: block;
  width: 100%;
  height: auto;
}

/* A static route-map screenshot reads as inert next to the rest of the
   page's motion (carousel, hover states, the smooth-scrolling logo), so it
   gets its own slow, ambient drift -- a gentle scale-and-drift "Ken Burns"
   loop rather than anything that reads as a deliberate transition. 14s and
   a small range (3% scale, a few px of drift) keep it in the background;
   .transfer-map's own overflow:hidden clips the scaled-up edges. Gated
   behind prefers-reduced-motion like every other animation on the page. */
@media (prefers-reduced-motion: no-preference) {
  .transfer-map img {
    animation: transfer-map-drift 14s ease-in-out infinite;
  }

  @keyframes transfer-map-drift {
    0% {
      transform: scale(1) translate(0, 0);
    }
    50% {
      transform: scale(1.035) translate(-0.6%, -1%);
    }
    100% {
      transform: scale(1) translate(0, 0);
    }
  }
}

/* At full section width (up to --max-width, 1120px) the 16:9 desktop crop
   renders around 630px tall, tall enough that it can push the paragraph
   and CTA button below the fold on common laptop viewport heights, so the
   map, the copy and the button are all capped to a narrower shared column
   instead. Left-aligned, that column just sat awkwardly off to one side
   with a wall of empty space beside it, so the whole block is centered as
   a unit -- matching how the guest-review quotes just below on the page
   are already centered rather than left-aligned. The eyebrow/heading were
   first left at the section's usual left edge for consistency with every
   other section's heading, but sitting directly above a visibly centered
   card that reads worse in practice than the cross-section inconsistency
   it was avoiding -- a heading floating off to one side above content
   that's clearly centered underneath it looks like a layout accident, not
   a deliberate choice, and it's the kind of mismatch you actually notice
   at a glance since both are in view together (unlike two *different*
   sections' headings, which are never on screen at the same time). So the
   eyebrow and heading now share the same 640px column as the map/copy/
   button, centered as one unit. Mobile is untouched (full width,
   left-aligned), which didn't have either problem at phone viewport
   widths. */
@media (min-width: 641px) {
  #transfer .eyebrow,
  #transfer h2 {
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
    text-align: center;
  }

  .transfer-content {
    max-width: 640px;
    margin: 0 auto;
    text-align: center;
  }
}

.host-profile__eyebrow {
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-steel);
}

.host-profile__name {
  margin: 0;
  font-family: var(--font-heading);
  font-size: 1.7rem;
  font-weight: 600;
  color: var(--color-navy);
}

.host-profile__greeting {
  margin: 0;
  color: var(--color-ink);
  max-width: 32em;
}

.host-profile__languages {
  margin: 0;
  font-size: var(--text-md);
}

.host-profile__languages-label {
  font-weight: 600;
  color: var(--color-navy);
  margin-right: 0.4em;
}

.host-profile__languages-value {
  color: var(--color-text-muted);
}

@media (max-width: 640px) {
  .hero {
    min-height: 55vh;
  }

  .hero-bar__inner {
    padding: 1rem 1.5rem;
  }

  .whatsapp-btn--hero {
    width: 100%;
    justify-content: center;
    margin-left: 0;
    margin-top: 0.25rem;
    /* Some languages' button text is longer than others (Croatian,
       Italian) — on a full-width mobile button that can be wider than
       the pill itself if it's forced onto one line, so it's allowed to
       wrap to a second line here instead of spilling out past the
       rounded edges. */
    white-space: normal;
    text-align: center;
    line-height: 1.3;
  }

  .main-bar {
    flex-wrap: wrap;
    padding: 1rem 1.25rem;
  }

  .whatsapp-btn--nav {
    display: none;
  }

  .lang-switcher {
    gap: 0.5rem;
  }

  /* At the default clamp, the hero title's minimum size (2.4rem) is
     already wider than a phone screen can fit on one line for a name
     this long, and on the narrowest phones it wraps a third time —
     "Luxury" / "Apartments Cuba" / "Novalja" — which reads as choppy
     for a headline. Sized down, it holds at two lines across the
     common phone widths instead. */
  .hero-title {
    font-size: 2.125rem;
  }

  /* The photo plus its title/tagline, and the direct-booking bar right
     under it, used to add up to whatever height their own content
     needed -- on a tall phone that left the section/tab bar peeking in
     at the very bottom of the first screen, which read as the layout
     just running out of room rather than a deliberate stopping point.
     Stretching the whole hero to fill the visible screen fixes that --
     but it has to be 100dvh, not 100svh: svh is a *fixed* number (the
     height when the browser's address bar is fully expanded), so the
     moment Safari's toolbar shrinks to its compact/floating mode on
     scroll -- which it does almost immediately -- the real viewport
     gets taller than that fixed number, and the section stops filling
     it, leaving the exact peeking-tab-bar gap this was meant to fix,
     just a bit further down. dvh instead tracks the *current* visible
     height live, growing and shrinking with the toolbar, so the section
     always matches whatever's actually on screen. min-height:100vh is
     a fallback for the handful of browsers with neither svh nor dvh;
     dvh overrides it wherever it's supported (which is effectively
     everywhere dvh matters, i.e. mobile Safari/Chrome). Desktop/tablet
     are untouched -- this is mobile-only, where the peeking-tab-bar
     problem existed in the first place. */
  .hero-section {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
  }

  .hero {
    /* Keeps its own aspect-ratio/min-height-derived size rather than
       being stretched or squeezed by the flex column above. */
    flex: 0 0 auto;
  }

  .hero-bar {
    /* Grows to take up whatever room .hero-section's new min-height
       adds beyond the photo's own height, instead of staying only as
       tall as the CTA content -- and centers that content vertically
       in the space it's given, rather than pinning it to the top with
       a growing gap underneath. position:relative so the scroll cue
       below can anchor to this box's bottom edge specifically, not the
       page's. */
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    position: relative;
  }

  .hero-bar__inner {
    width: 100%;
  }

  .hero-cta {
    /* A bit more breathing room between the message and the button now
       that the bar isn't just wrapping tightly around them. */
    gap: 1.75rem;
  }

  /* The glass-card framing on .hero-cta__card (further up) exists to
     separate the discount message from the navy bar when that bar is
     only ever as tall as its own content -- but now that the bar fills
     the rest of the screen above, the message already has the whole
     dark area as its backdrop, so a boxed card around a couple of
     short lines just reads as an odd little frame floating in a lot of
     empty navy, not "nicely laid out". Dropping the box lets the
     eyebrow/headline sit directly on the bar instead -- the same
     "plain text on a dark backdrop" treatment the hero title and
     tagline already use on the photo just above, so the whole first
     screen reads as one consistent language rather than a borderless
     top half and a boxed bottom half.

     The badge and headline also stack (eyebrow on its own line, then
     the headline below) instead of sitting side by side -- the same
     eyebrow-then-heading rhythm already used everywhere else on the
     page (Location/Surroundings, Guest Reviews, Meet Your Host), and
     it reads as a considered layout rather than a squeezed-together
     leftover from the card version, where side-by-side was only there
     to fit both on one line inside a narrow box. */
  .hero-cta__card {
    background: none;
    border: none;
    padding: 0;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
  }

  /* Both sized up from the boxed-card version: that sizing was tuned to
     fit comfortably inside a small pill-shaped card, but stacked on an
     open dark backdrop with real room to breathe, the same small sizes
     read as leftover rather than intentional -- the point of freeing
     this text from its box was for it to actually use the space. */
  .hero-cta__badge {
    font-size: 0.8rem;
    letter-spacing: 0.15em;
  }

  .hero-cta__headline {
    font-size: 1.4rem;
    line-height: 1.35;
  }

  .hero-scroll-cue {
    display: block;
    position: absolute;
    left: 50%;
    bottom: 1.1rem;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, 0.65);
    line-height: 0;
    pointer-events: none;
  }

  @media (prefers-reduced-motion: no-preference) {
    .hero-scroll-cue {
      animation: hero-scroll-cue-bounce 1.8s ease-in-out infinite;
    }
  }

}
