/* ============================================================================
   04-BLOCKS — iOS-native components

   Each block is composed from primitives and reads tokens. No block hardcodes
   a colour, size, or spacing value, so a theme swap changes every block at
   once and cannot desynchronise them.
   ============================================================================ */

/* ---------------------------------------------------------------- NAVBAR ---
   iOS frosted navigation bar. Translucent, saturated backdrop blur —
   the single most recognisable iOS surface treatment.                       */
.navbar {
  position: sticky;
  inset-block-start: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-block-end: 0.5px solid var(--separator);
}
@supports not (backdrop-filter: blur(1px)) {
  .navbar { background: var(--bg); }
}
.navbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  min-block-size: 3rem;
  padding-block: var(--space-xs);
}
.navbar__brand {
  font-weight: var(--weight-semibold);
  font-size: var(--type-headline);
  color: var(--label);
  text-decoration: none;
  letter-spacing: var(--track-title);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-block-size: var(--tap-min);
}
/* The site mark, from marks.mjs — the same glyph the favicon and wordmark use,
   so one tour has one identity everywhere. Rounded tile keeps it reading as a
   logo rather than as another UI icon. */
.navbar__mark {
  flex: none;
  /* Sized in em against the brand text so the tile is exactly as tall as the
     wordmark beside it — a fixed rem value drifts the moment type scale moves. */
  inline-size: 1.45em; block-size: 1.45em;
  border-radius: 0.42em;
  background: var(--accent);
  color: var(--on-accent);
  display: grid; place-items: center;
}
.navbar__mark svg { inline-size: 68%; block-size: 68%; }
.footer__mark {
  inline-size: 2.25rem; block-size: 2.25rem;
  border-radius: 10px;
  background: var(--accent); color: var(--on-accent);
  display: grid; place-items: center;
  margin-block-end: var(--space-xs);
}
.footer__mark svg { inline-size: 1.5rem; block-size: 1.5rem; }
/* The full site name wraps the bar to two lines on a phone. Swap to a short
   form below 30rem — same link, less string. */
.navbar__brand-short { display: none; }
@media (max-width: 30rem) {
  .navbar__brand-full { display: none; }
  .navbar__brand-short { display: inline; }
}

/* Phone bar: mark hard left, title optically centred, burger hard right.
   The title is positioned against .navbar__inner rather than laid out in flow,
   because centring it in flow would only centre it in the space the mark and
   burger leave over — which is not the middle of the bar. Inset clears both
   controls so they stay tappable. */
@media (max-width: 46rem) {
  .navbar__inner { position: relative; }
  /* Once the title is positioned out of flow the link is just the 25px mark. */
  .navbar__brand { min-inline-size: var(--tap-min); }
  .navbar__brand-short,
  .navbar__brand-full {
    position: absolute;
    inset-inline: 3.5rem;
    text-align: center;
    pointer-events: none;
  }
  /* No globe in the bar on a phone — language lives in the menu. */
  .navbar .langswitch--compact { display: none; }
}
.navbar__links { font-size: var(--type-subhead); --cluster-gap: var(--space-xs); }
/* Vertical padding buys the 44px HIG tap height without making the bar taller
   or the text bigger — the auditor flagged these at 40×23px. */
.navbar__links a {
  color: var(--label-secondary);
  text-decoration: none;
  font-weight: var(--weight-medium);
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap-min);
  /* Short words ("FAQ", "Book") were 35-43px wide against a 44px minimum.
     Height alone is not the target — both axes are. Desktop-only: below the
     wrap point these links live in the mobile menu, which sizes its own rows. */
  min-inline-size: var(--tap-min);
  justify-content: center;
  padding-inline: var(--space-2xs);
}
.navbar__links a:hover, .navbar__links a[aria-current] { color: var(--accent); }

/* ------------------------------------------------------------------ HERO ---
   iOS Large Title. Left-aligned, heavy, tight tracking — not the centred
   hero-with-gradient that reads as generic.                                 */
/* A hero is a room you stand in, not a photo with a caption underneath. The
   image fills the section, a full-surface scrim carries the type, and the
   primary CTA lives here rather than below the fold.
   svh not vh: on iOS, vh measures the viewport WITHOUT browser chrome, so a
   100vh hero is taller than the screen and pushes the CTA out of view. */
.hero {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: end;
  min-block-size: clamp(30rem, 82svh, 46rem);
  color: #fff;
  overflow: hidden;
}
.hero__media { position: absolute; inset: 0; z-index: -1; }
.hero__media img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.hero__media::after {
  content: "";
  position: absolute; inset: 0;
  /* Two layers: an even wash so type is legible over ANY photo, plus a bottom
     weight so the content end stays anchored. One gradient cannot do both. */
  background:
    linear-gradient(to top, rgb(var(--scrim-rgb) / 0.82) 0%, rgb(var(--scrim-rgb) / 0.42) 52%, rgb(var(--scrim-rgb) / 0.28) 100%),
    linear-gradient(rgb(var(--scrim-rgb) / 0.38), rgb(var(--scrim-rgb) / 0.38));
}
/* inline-size:100% is load-bearing. .center centres with margin-inline:auto,
   and auto margins on a GRID ITEM disable stretch — the box then shrinks to its
   widest child and the whole hero collapses to the h1's measure. */
/* Content sits ~20% higher in the frame: the block is optically centred rather
   than bottom-weighted, which left the type sinking into the lower third. */
.hero { align-items: center; }
/* Optically centred, then nudged down: more padding above than below moves
   the block toward the lower half without letting it sink to the floor. */
.hero__body { inline-size: 100%; padding-block: var(--space-4xl) var(--space-2xl); text-align: center; }
.hero__body .stack { align-items: center; }
.hero__body .cluster { --cluster-justify: center; }
.hero__body h1 {
  color: #fff;
  font-size: clamp(2.25rem, 1.1rem + 4.8vw, 4.25rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  text-wrap: balance;
  max-inline-size: 18ch;
  margin-inline: auto;
}
.hero__kicker {
  display: inline-flex; align-items: center;
  align-self: center;  /* .stack is a flex column; without this the pill stretches */
  inline-size: fit-content;
  padding: 0.4rem 0.95rem;
  border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.12);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.38);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  font-size: var(--type-caption);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: #fff;
}
.hero__sub {
  color: rgba(255,255,255,0.92);
  font-size: var(--type-title-3);
  font-weight: var(--weight-regular);
  line-height: var(--leading-snug);
  max-inline-size: 52ch;
  margin-inline: auto;
}
.hero__rating {
  display: flex; align-items: center; justify-content: center;
  gap: var(--space-xs); flex-wrap: wrap;
  font-size: var(--type-footnote);
  color: rgba(255,255,255,0.90);
}
.hero__rating > span { display: inline-flex; align-items: center; gap: 0.35rem; }
.hero__rating svg { color: rgba(255,255,255,0.72); flex: none; }
.hero__rating-sep {
  inline-size: 1px; block-size: 0.85em;
  background: rgba(255,255,255,0.35);
}
@media (max-width: 26rem) { .hero__rating-sep { display: none; } }
.hero__cta { --cluster-gap: var(--space-s); padding-block-start: var(--space-xs); }
/* Secondary action on top of imagery: a glass pill, not a grey button that
   disappears into the photo. */
.btn--glass {
  background: rgba(255,255,255,0.18);
  color: #fff;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.30);
}
.btn--glass:hover { background: rgba(255,255,255,0.28); color: #fff; }

/* ------------------------------------------------------- STAT STRIP (B2) ---
   Trust signals. Pulled up over the hero — the guidetohana overlap trick. */
.statstrip {
  position: relative;
  margin-block-start: calc(var(--space-xl) * -1);
  z-index: 2;
}
.statstrip__inner {
  display: grid;
  /* Four fixed columns, never auto-fit. auto-fit dropped the fourth stat onto a
     second row on a phone, where it also hyphenated mid-word ("Year roun/d").
     Below the breakpoint we show three and drop the least load-bearing one. */
  grid-template-columns: repeat(4, 1fr);
  background: var(--bg-elevated);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
.stat {
  padding: var(--space-m) var(--space-s);
  text-align: center;
  border-inline-end: 0.5px solid var(--separator);
}
.stat:last-child { border-inline-end: 0; }
/* Never break a stat value mid-word — the global hyphens:auto rule was
   splitting "Year round" across two lines inside a 90px column. */
.stat__value, .stat__label { overflow-wrap: normal; hyphens: none; text-wrap: balance; }
.stat__value {
  display: block;
  font-family: var(--font-display);
  font-size: var(--type-title-2);
  font-weight: var(--weight-bold);
  letter-spacing: var(--track-title);
  line-height: 1.1;
}
.stat__label {
  display: block;
  font-size: var(--type-caption);
  color: var(--label-secondary);
  margin-block-start: 2px;
}

/* -------------------------------------------------- INSET GROUPED LIST -----
   THE iOS pattern — the Settings app. White card on tinted page, hairline
   separators inset from the left, optional chevron. Used for facts,
   inclusions, practical info, and anything key/value.                       */
.inset-list {
  background: var(--bg-elevated);
  border-radius: var(--radius-m);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  list-style: none;
  margin: 0;
  padding: 0;
}
.list-row {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s) var(--space-m);
  position: relative;
  text-decoration: none;
  color: var(--label);
}
/* Separator inset from the left, exactly as iOS draws it */
.list-row + .list-row::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: var(--space-m) 0;
  block-size: 0.5px;
  background: var(--separator);
}
.list-row__icon > svg { inline-size: 1.35rem; block-size: 1.35rem; }
/* No tinted rounded tile. That shape behind every icon is the same generated-
   page marker as the pastel callout — the glyph alone is more premium and
   reads faster. Slightly larger, one weight, no container. */
.list-row__icon {
  flex: 0 0 1.5rem;
  block-size: 1.5rem;
  display: grid;
  place-items: center;
  color: var(--accent);
}
.list-row__label { flex: 1 1 auto; font-size: var(--type-body); }
.list-row__value {
  flex: 0 0 auto;
  color: var(--label-secondary);
  font-size: var(--type-body);
  text-align: end;
}
a.list-row::after {
  content: "";
  flex: 0 0 auto;
  inline-size: 7px; block-size: 7px;
  border-inline-end: 2px solid var(--label-tertiary);
  border-block-start: 2px solid var(--label-tertiary);
  transform: rotate(45deg);
  border-radius: 1px;
}
a.list-row:hover { background: var(--bg-tertiary); }

/* ------------------------------------------------------ TOUR CARD (C1) -----
   Our own markup — crawlable, themeable, zero JS. Not the GYG iframe.      */
.tour-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-elevated);
  border-radius: var(--radius-l);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.tour-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.tour-card__media { --frame-ratio: 3 / 2; }
.tour-card__badge {
  position: absolute;
  inset-block-start: var(--space-s);
  inset-inline-start: var(--space-s);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: var(--label);
  font-size: var(--type-caption);
  font-weight: var(--weight-semibold);
  padding: 0.25rem 0.5rem;
  border-radius: var(--radius-pill);
  z-index: 1;
}
.tour-card__body { padding: var(--space-m); display: flex; flex-direction: column; gap: var(--space-xs); flex: 1 1 auto; }
.tour-card__title {
  font-size: var(--type-headline);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-body);
}
.tour-card__meta {
  font-size: var(--type-footnote);
  color: var(--label-secondary);
  --cluster-gap: var(--space-xs) var(--space-s);
}
.tour-card__meta > * { display: inline-flex; align-items: center; gap: 0.25rem; }
.rating { color: var(--label); font-weight: var(--weight-semibold); }
.rating__star { color: var(--star); }
.tour-card__foot {
  margin-block-start: auto;
  padding-block-start: var(--space-s);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-s);
  flex-wrap: wrap;
}
.price { line-height: 1.1; }
.price__from { display: block; font-size: var(--type-caption); color: var(--label-secondary); }
.price__value { font-size: var(--type-title-3); font-weight: var(--weight-bold); letter-spacing: var(--track-title); }

/* ---------------------------------------------------------- BUTTONS --------
   iOS filled button. CTA colour is deliberately not the accent — it must
   contrast against the page, never blend into it.                          */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.7rem 1.15rem;
  border: 0;
  border-radius: var(--radius-m);
  font-size: var(--type-headline);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--track-body);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: scale(0.97); }   /* iOS press feedback */
.btn--cta { background: var(--cta); color: var(--on-cta); box-shadow: var(--cta-shadow); }
.btn--cta:hover { background: var(--cta-hover); color: var(--on-cta); }
.btn--filled { background: var(--accent); color: var(--on-accent); }
.btn--filled:hover { background: var(--accent-hover); color: var(--on-accent); }
.btn--tinted { background: var(--accent-soft); color: var(--accent); }
.btn--plain { background: transparent; color: var(--accent); padding-inline: 0.25rem; }
.btn--block { inline-size: 100%; }
.btn--lg { padding: 0.9rem 1.5rem; font-size: var(--type-title-3); }

/* ---------------------------------------------------- ITINERARY (E6) -------
   CSS-only. Apple Maps directions: dots on a rail, connected by a line.
   No Maps embed, no API key, no external request.                          */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline__item {
  position: relative;
  padding-inline-start: var(--space-2xl);
  padding-block-end: var(--space-l);
}
.timeline__item:last-child { padding-block-end: 0; }
/* the rail */
.timeline__item::before {
  content: "";
  position: absolute;
  inset-inline-start: 0.4375rem;
  inset-block: 0.55rem 0;
  inline-size: 2px;
  background: var(--separator);
}
.timeline__item:last-child::before { display: none; }
/* the dot */
.timeline__item::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.35rem;
  inline-size: 1rem; block-size: 1rem;
  border-radius: 50%;
  background: var(--bg-elevated);
  border: 3px solid var(--accent);
  box-sizing: border-box;
}
.timeline__item[data-terminus]::after { background: var(--accent); }
.timeline__title { font-size: var(--type-headline); font-weight: var(--weight-semibold); }
.timeline__meta {
  font-size: var(--type-footnote);
  color: var(--label-secondary);
  margin-block-start: 2px;
}
.timeline__dur {
  display: inline-block;
  font-size: var(--type-caption);
  font-weight: var(--weight-semibold);
  color: var(--accent);
  background: var(--accent-soft);
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-pill);
  margin-inline-start: 0.35rem;
}

/* ------------------------------------------------- TESTIMONIALS (F1) -------
   Scroll-snap reel. One card visible, ~10% of the next peeking.            */
.testimonial {
  background: var(--bg-elevated);
  border-radius: var(--radius-l);
  padding: var(--space-m);
  box-shadow: var(--shadow-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}
.testimonial__quote { font-size: var(--type-callout); line-height: var(--leading-body); }
.testimonial__who { display: flex; align-items: center; gap: var(--space-s); margin-block-start: auto; }
.testimonial__avatar {
  flex: 0 0 2.5rem;
  inline-size: 2.5rem; block-size: 2.5rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--gray-5);
}
.testimonial__name { font-size: var(--type-subhead); font-weight: var(--weight-semibold); line-height: 1.2; }
.testimonial__origin { font-size: var(--type-caption); color: var(--label-secondary); }
.testimonial__stars { color: var(--star); font-size: var(--type-footnote); letter-spacing: 0.06em; }

/* ------------------------------------------------- COMPARISON (C3) ---------
   Rows = attributes, columns = products. On a narrow container the axis
   flips: each attribute becomes its own labelled block with the products
   side by side inside it — because on a phone people compare one attribute
   at a time, not one product at a time.                                    */
.compare-wrap { container-type: inline-size; }
.compare {
  inline-size: 100%;
  border-collapse: collapse;
  background: var(--bg-elevated);
  border-radius: var(--radius-m);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.compare th, .compare td {
  padding: var(--space-s) var(--space-m);
  text-align: start;
  font-size: var(--type-subhead);
  vertical-align: top;
  border-block-start: 0.5px solid var(--separator);
}
.compare thead th {
  border-block-start: 0;
  font-weight: var(--weight-semibold);
  font-size: var(--type-footnote);
  color: var(--label-secondary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.compare tbody th { font-weight: var(--weight-semibold); color: var(--label); inline-size: 8rem; }
.compare td { font-weight: var(--weight-medium); }
/* Light green cell fill and nothing else. The rule down the edge read as a
   second, competing signal next to the tint. */
.compare [data-best] {
  font-weight: var(--weight-semibold);
  color: var(--label);
  background: color-mix(in srgb, var(--success) 12%, transparent);
}

@container (max-width: 34rem) {
  .compare, .compare thead, .compare tbody, .compare tr, .compare th, .compare td { display: block; }
  .compare thead { display: none; }
  .compare tr { padding: var(--space-s) 0; border-block-start: 0.5px solid var(--separator); }
  .compare tr:first-child { border-block-start: 0; }
  .compare tbody th {
    inline-size: auto; border: 0;
    padding-block-end: var(--space-2xs);
    font-size: var(--type-footnote); text-transform: uppercase; letter-spacing: 0.05em;
  }
  .compare tr { display: grid; grid-template-columns: 1fr; }
  .compare tbody td {
    display: block; border: 0;
    padding-block: 2px;
  }
  .compare tbody td::before {
    content: attr(data-label) " · ";
    color: var(--label-tertiary);
    font-weight: var(--weight-regular);
  }
}

/* ------------------------------------------------------------- FAQ ---------
   iOS disclosure rows inside an inset list.                                */
.faq { background: var(--bg-elevated); border-radius: var(--radius-m); overflow: hidden; box-shadow: var(--shadow-sm); }
.faq__item + .faq__item { border-block-start: 0.5px solid var(--separator); }
.faq__q {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-s);
  padding: var(--space-s) var(--space-m);
  font-size: var(--type-body);
  font-weight: var(--weight-medium);
  cursor: pointer;
  list-style: none;
}
/* The question is ONE flex item however it is marked up. Any inline element
   inside a flex container becomes a sibling item, so a single link split the
   text into three and `space-between` spread them across the row. */
.faq__q > * { flex: 0 1 auto; }
.faq__q::-webkit-details-marker { display: none; }
.faq__q::after {
  content: "";
  flex: 0 0 auto;
  inline-size: 8px; block-size: 8px;
  border-inline-end: 2px solid var(--label-tertiary);
  border-block-end: 2px solid var(--label-tertiary);
  transform: rotate(45deg) translateY(-2px);
  transition: transform var(--dur) var(--ease);
}
.faq__item[open] .faq__q::after { transform: rotate(-135deg) translateY(-2px); }
.faq__a { padding: 0 var(--space-m) var(--space-m); color: var(--label-secondary); font-size: var(--type-callout); }

/* ------------------------------------------------------- CALLOUT (D6/D7) --- */
/* NO coloured left bar. Ever. It is the single loudest "AI-generated page"
   tell and it has been rejected repeatedly. A callout is a quiet tinted
   surface with a hairline — the tint alone carries the meaning. */
/* No tinted-panel-with-border. That shape — pastel fill, rounded box, coloured
   edge — is the single most recognisable generated-content marker there is.
   This is an editorial aside instead: same surface as the page, separated by
   rules, with a small uppercase label doing the signalling. */
.callout {
  padding-block: var(--space-m);
  border-block: 1px solid var(--separator);
  font-size: var(--type-callout);
  color: var(--label);
}
.callout__label {
  display: block;
  font-size: var(--type-caption);
  font-weight: var(--weight-semibold);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--label-secondary);
  margin-block-end: var(--space-2xs);
}
.callout--warn .callout__label { color: var(--cta-ink); }   /* ink, not fill — see 01-tokens */
.callout--warn { border-block-color: color-mix(in srgb, var(--cta) 32%, var(--separator)); }
.callout__title { font-weight: var(--weight-semibold); color: var(--label); margin-block-end: 2px; }

/* --------------------------------------------------------- FACT BAND (D5) -- */
/* Dramatic without being loud: a deep plate, an oversized number, and two
   slow-drifting light sources. The motion is 22-28s and low-contrast — at that
   speed it reads as depth rather than animation, which is the point.
   Everything stops under prefers-reduced-motion. */
.factband {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background-color: color-mix(in srgb, var(--accent) 68%, #000);
  background-image: linear-gradient(150deg,
    color-mix(in srgb, var(--accent) calc(var(--plate-mix) + 18%), #000) 0%,
    color-mix(in srgb, var(--accent) calc(var(--plate-mix) - 26%), #000) 62%,
    color-mix(in srgb, var(--accent) calc(var(--plate-mix) - 10%), #000) 100%);
  color: #fff;
  border-radius: var(--radius-xl);
  padding: var(--space-4xl) var(--space-l);
  text-align: center;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.16), var(--shadow-lg);
}
.factband::before,
.factband::after {
  content: "";
  position: absolute; z-index: -1;
  border-radius: 50%;
  pointer-events: none;
  will-change: transform;
}
.factband::before {
  inline-size: 34rem; block-size: 34rem;
  inset-block-start: -46%; inset-inline-end: -14%;
  background: radial-gradient(circle at 34% 34%, rgba(255,255,255,0.20), transparent 62%);
  animation: fb-drift-a 24s ease-in-out infinite alternate;
}
.factband::after {
  inline-size: 26rem; block-size: 26rem;
  inset-block-end: -40%; inset-inline-start: -12%;
  background: radial-gradient(circle at 60% 40%, color-mix(in srgb, var(--accent) 70%, #fff) 0%, transparent 64%);
  opacity: .34;
  animation: fb-drift-b 28s ease-in-out infinite alternate;
}
@keyframes fb-drift-a {
  from { transform: translate3d(0,0,0) scale(1); }
  to   { transform: translate3d(-5%, 7%, 0) scale(1.12); }
}
@keyframes fb-drift-b {
  from { transform: translate3d(0,0,0) scale(1.06); }
  to   { transform: translate3d(7%, -6%, 0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .factband::before, .factband::after { animation: none; }
}
/* The number IS the block, so it is set as display type, not body copy.
   (An earlier edit deleted these rules and the follow-up replace silently
   no-opped — hence the assert-before-write discipline in the edit scripts.) */
.factband__value {
  position: relative; z-index: 1;
  font-family: var(--font-display);
  font-size: clamp(3.25rem, 1.6rem + 7vw, 6.5rem);
  font-weight: var(--weight-bold);
  letter-spacing: -0.035em;
  line-height: 0.95;
  color: #fff;
}
.factband__text {
  position: relative; z-index: 1;
  color: rgba(255,255,255,0.88);
  font-size: var(--type-title-3);
  line-height: var(--leading-snug);
  margin-block-start: var(--space-m); margin-inline: auto;
  max-inline-size: 42ch;
  text-wrap: balance;
}

/* ---------------------------------------------------------- STICKY BAR (C7)-
   Mobile booking bar. Present on none of the 9 reference sites.            */
.bookbar {
  position: fixed;
  inset-block-end: 0; inset-inline: 0;
  z-index: 90;
  padding: var(--space-s) var(--gutter) calc(var(--space-s) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-block-start: 0.5px solid var(--separator);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-m);
}
/* Social proof replaces the price: prices may only come from a GYG widget.
   Centred on the cross axis so it lines up with the middle of the button. */
.bookbar__proof { display: flex; flex-direction: column; justify-content: center; gap: 1px; }
.bookbar__score { font-weight: var(--weight-bold); font-size: var(--type-callout);
  color: var(--label); display: inline-flex; align-items: center; gap: 0.25rem; }
.bookbar__score::first-letter { color: var(--star); }
.bookbar__count { font-size: var(--type-caption); color: var(--label-secondary); }

@media (min-width: 48rem) { .bookbar { display: none; } }
body:has(.bookbar) { padding-block-end: 5rem; }

/* -------------------------------------------------------------- FOOTER ----- */
.footer {
  background: var(--bg-elevated);
  border-block-start: 0.5px solid var(--separator);
  padding-block: var(--space-2xl);
  font-size: var(--type-subhead);
  color: var(--label-secondary);
}
/* Footer links are the most-tapped small targets on a phone. Padding gives
   them the 44px HIG height without changing how they look. */
.footer a {
  color: var(--label-secondary);
  text-decoration: none;
  /* display:block, not flex. A flex container turns its text into an anonymous
     flex item that `overflow-wrap` on the <a> no longer reaches — which is how
     "Hengjanefossen" pushed the footer 12px wide at 360px. Padding + min-height
     buys the 44px HIG target while normal text flow keeps long words wrapping. */
  display: block;
  padding-block: 0.6rem;
  min-block-size: var(--tap-min);
}
.footer a:hover { color: var(--accent); }
.footer h4 { color: var(--label); margin-block-end: var(--space-xs); }

/* -------------------------------------------------------------------------
   THE LINK LIST — ONE RHYTHM, WHATEVER THE LABEL DOES.

   Spacing came from three sources at once: a stack gap, 9.6px of padding, and a
   44px min-block-size. A one-line link was 44px (1.55px taller than its own
   content) while a two-line link was 65.7px, so the gap between labels changed
   depending on whether the one above it wrapped - which is exactly the unevenness
   you see once a translated title runs long.

   Padding alone now sets it. The gap between two labels is always twice the
   padding, wrapped or not, and 0.65rem still clears the 44px tap target on a
   single line: 23.25px of line box + 20.8px of padding = 44.05px. No min-height
   to fight with, no list gap to add a fourth number.
   ------------------------------------------------------------------------- */
.footer__nav ul { list-style: none; margin: 0; padding: 0; }

/* SINGLE COLUMN, six links. Two columns balanced the block heights but split a
   short list across the page, and a list of six reads better as one run. */
/* THE GAP IS ARITHMETIC, not a style choice: a 44px row around a 16px glyph
   leaves 28px between labels however the space is distributed. Tightening the
   list therefore means shorter rows, and a shorter row is a smaller tap target.

   Split by INPUT DEVICE rather than by screen width. A finger needs 44px and a
   cursor does not, so touch keeps the HIG target and pointer devices get the
   tighter rhythm - the list reads as a list instead of a stack of buttons. */
.footer { --foot-link-pad: 0.3rem; }
@media (pointer: coarse) { .footer { --foot-link-pad: 0.65rem; } }

.footer__nav a {
  padding-block: var(--foot-link-pad);
  min-block-size: 0;
  overflow-wrap: anywhere;
}
/* The heading's gap is measured to the first LABEL, not to the box around it,
   so it subtracts that link's top padding - from the same custom property, or
   the two drift apart the moment one of them is retuned. */
.footer__nav h4 { margin-block-end: calc(var(--space-s) - var(--foot-link-pad)); }

/* -------------------------------------------------------- FOOTER (A6) ------
   Was three link columns and one grey paragraph. A footer is the last trust
   surface on the page: it carries the language switch, the affiliate
   disclosure, and the signals that say a real site is behind this.          */
/* A GRID, so the two columns share rows instead of merely starting together.

   As a flex row the mark was the first thing in the brand column, which put the
   brand name 49px below the "Guides" heading beside it - two columns whose text
   began on different lines. The mark now occupies row 1 on its own and the two
   text columns share row 2, so they align by construction rather than by a
   margin someone has to keep in step with the mark's height.

   `align-items: start` stops the short column stretching to the tall one, which
   is what left a block of dead space under the trust list. */
/* PLACED BY ROW AND COLUMN, NOT BY NAMED AREA.

   With areas, both nav columns claimed the same "nav" and landed on identical
   coordinates - the About column rendered exactly on top of Guides, which reads
   as a single column in the markup and is invisible to a text check. Row 2 is
   declared once and the navs auto-place across the remaining columns, so a third
   column is a column count away rather than another name to invent. */
.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 28rem) repeat(2, minmax(0, 1fr));
  align-items: start;
  column-gap: var(--space-2xl);
  row-gap: var(--space-m);
}
/* THREE ROWS: the mark, the columns, and the disclosure.

   The disclosure sits in column 1 of row 3 while both navs SPAN rows 2 and 3,
   so the grid's bottom edge is the bottom of the tallest nav. Ending the
   disclosure against that edge puts its last line level with the last link in
   the Guides column, which is what was asked for - and it does it by
   construction, at any number of links and in any language, rather than by a
   margin someone has to retune. */
.footer__top > .footer__mark { grid-column: 1; grid-row: 1; }
/* BASELINE, not box top. The brand name is 20px type and the nav heading is 17px,
   so aligning their boxes still left the two words sitting 5px apart - aligned to
   a machine, visibly off to a reader. Baseline alignment puts the two lines of
   text on the same line, which is what "aligned" means for type. */
.footer__brand { grid-column: 1; grid-row: 2; align-self: baseline; }
.footer__nav   { grid-row: 2 / span 2; align-self: baseline; }
.footer__top > .footer__disclosure {
  grid-column: 1; grid-row: 3;
  align-self: end;
  margin-block-start: var(--space-l);
  /* The grid aligns BOXES; a reader aligns the last LINES. A nav link carries
     its tap padding below its text, so ending both boxes on the same edge still
     left the disclosure's last line 5px lower. Matching that padding puts the
     two final lines on the same level - and it reads the same custom property,
     so it cannot drift when the link rhythm is retuned. */
  padding-block-end: var(--foot-link-pad);
}

/* One column below 48rem: two 16rem columns do not fit a phone, and the nav
   reads as a continuation of the brand block rather than a sidebar. */
/* One column below 48rem: three columns do not fit a phone, and the navs read as
   a continuation of the brand block rather than as sidebars. Row placement is
   cleared so everything stacks in source order. */
@media (max-width: 48rem) {
  .footer__top { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-l); }
  .footer__top > * { grid-column: 1; grid-row: auto; align-self: start; }
}
.footer__name {
  font-size: var(--type-title-3); font-weight: var(--weight-bold);
  color: var(--label); letter-spacing: var(--track-title);
}
.footer__tag { margin-block-start: var(--space-2xs); }

/* Trust row — icon + label pairs. Not badge images: an image of a badge is
   both a request and a claim we cannot substantiate. These state facts. */
.footer__trust {
  display: flex; flex-wrap: wrap; gap: var(--space-xs) var(--space-l);
  margin-block-start: var(--space-m);
}
.footer__trust > span {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: var(--type-footnote); color: var(--label-secondary);
}
.footer__trust svg { color: var(--success); flex: none; }

.footer__bottom {
  margin-block-start: var(--space-xl);
  padding-block-start: var(--space-m);
  border-block-start: 0.5px solid var(--separator);
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: var(--space-s) var(--space-m);
}
/* --label-tertiary measured 1.80:1 here. */
.footer__legal { font-size: var(--type-caption); color: var(--label-secondary); margin-inline-start: auto; }
.footer__disclosure {
  font-size: var(--type-caption); color: var(--label-secondary);
  max-inline-size: 62ch; margin-block-start: var(--space-xs);
}
/* The footer language switch opens UPWARD — a downward menu here would open
   off the bottom of the document. */
.footer .langswitch__menu { inset-block-start: auto; inset-block-end: 100%; margin-block-end: var(--space-2xs); }

/* Phone: three stats in one row. The fourth is the weakest of the four and its
   content also appears in the weather block, so nothing is lost. */
@media (max-width: 40rem) {
  .statstrip__inner { grid-template-columns: repeat(3, 1fr); }
  .statstrip__inner .stat:nth-child(4) { display: none; }
  .statstrip__inner .stat:nth-child(3) { border-inline-end: 0; }
  .stat { padding: var(--space-s) var(--space-2xs); }
  .stat__value { font-size: var(--type-title-3); }
}

/* A transfer leg is not a destination. Smaller dot, muted title, no bold — the
   eye skips it and lands on the next real stop, which is the point. */
.timeline__item[data-leg] .timeline__title {
  font-weight: var(--weight-regular);
  font-size: var(--type-subhead);
  color: var(--label-secondary);
}
/* ::after is the DOT. ::before is the connector LINE — overriding that one
   turned every leg's line into an 8px stub and broke the whole spine. */
.timeline__item[data-leg]::after {
  inline-size: 0.6rem; block-size: 0.6rem;
  inset-block-start: 0.55rem;
  inset-inline-start: 0.2rem;
  background: var(--bg-grouped);
  border: 2px solid var(--gray-4);
}

/* The header toggle sits beside the language switcher; on a phone the bar has
   no room for it and it moves into the menu instead. */
/* Utils sit together at the trailing edge — a toggle floating alone in the
   middle of the bar reads as unrelated to anything. */
.navbar__utils { display: inline-flex; align-items: center; gap: var(--space-2xs); }
/* display:none must outrank .themetoggle's display:inline-flex, which lives in
   a later file at equal specificity — hence the doubled class. */
@media (max-width: 46rem) { .navbar__utils .navbar__theme.navbar__theme { display: none; } }
/* Dark mode sits UNDER the language block, separated by a rule — it is a
   setting, not a navigation item. */
.site-menu__theme {
  margin-block-start: var(--space-xl);
  padding-block-start: var(--space-m);
  border-block-start: 0.5px solid var(--separator);
  inline-size: 100%; justify-content: space-between;
}
