/* Voyage — selling a place as much as a product.
   ---------------------------------------------------------------------------------------------
   The structure is the travel-booking archetype, and each piece of it is load-bearing:

   * a **utility strip** above everything for the phone number and the email, because somebody
     booking a trip wants to talk to a human before they pay;
   * a **transparent header with the brand centred**, sitting over the photograph rather than on
     a bar above it;
   * a full-bleed opening image with the headline centred in a **serif**;
   * a **search panel floating over the hero's bottom edge**, half on the picture and half on the
     page — the single most recognisable thing about this kind of front page;
   * section headings as a small accent eyebrow over a large serif line, with a **round link
     button** floating at the right;
   * cards whose **caption panel is inset and overlapping the photograph**, carrying a rating
     line and a place under the title;
   * a **deep inverted footer**.

   The colours are deliberately not the source's olive-and-gold: this is plum ink with a teal
   accent, so the arrangement is the borrowed idea rather than the palette.

   Scoping: every selector is `body.theme-voyage`. See vault's stylesheet for why. */

/* --------------------------------------------------------------------------------- tokens */
body.theme-voyage {
  --hw-brand: 600; --hw-link: 500; /* this design's own header weights; Design → header_weight may only raise them */
  --c-bg: var(--color-surface);
  --c-text: var(--color-ink);
  --c-primary: var(--color-ink);
  --c-accent: var(--color-accent);
  --c-muted: var(--color-ink-muted);
  --c-line: var(--color-border);
  --c-line-strong: color-mix(in srgb, var(--color-border) 60%, var(--color-ink));
  --c-soft: var(--color-surface-raised);
  --c-accent-soft: color-mix(in srgb, var(--color-accent) 12%, var(--color-surface));
  --c-shadow: 0 20px 50px color-mix(in srgb, var(--color-ink) 16%, transparent);
  --c-radius: 4px;
  --c-radius-sm: 3px;

  --vy-shell: 80rem;
  --vy-radius: 4px;
  --vy-gap: clamp(3rem, 6vw, 5.5rem);

  background: var(--color-surface);
  color: var(--color-ink);
  font-family: var(--font-body, Inter, system-ui, sans-serif);
  line-height: 1.7; margin: 0;
}
body.theme-voyage, body.theme-voyage *, body.theme-voyage *::before, body.theme-voyage *::after {
  box-sizing: border-box;
}
body.theme-voyage :where(a) { color: inherit; text-decoration: none; }
body.theme-voyage :where(img) { max-width: 100%; display: block; }
body.theme-voyage h1, body.theme-voyage h2, body.theme-voyage h3, body.theme-voyage h4 {
  font-family: var(--font-heading, "Playfair Display", Georgia, serif);
  margin: 0; font-weight: 500; line-height: 1.18; letter-spacing: -0.005em;
}
body.theme-voyage p { margin: 0; }
body.theme-voyage .container { width: min(var(--vy-shell), calc(100% - 2.5rem)); margin-inline: auto; }
body.theme-voyage .section { padding: var(--vy-gap) 0; }
body.theme-voyage .eyebrow {
  font-size: 0.72rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--color-accent); font-weight: 700; margin-bottom: 0.65rem;
}
body.theme-voyage .lead { color: var(--color-ink-muted); max-width: 52ch; }
body.theme-voyage .muted, body.theme-voyage .c-muted { color: var(--color-ink-muted); }
body.theme-voyage .text-link { color: var(--color-accent); font-weight: 600; font-size: 0.9rem; }
body.theme-voyage .text-link:hover { text-decoration: underline; text-underline-offset: 4px; }

/* --------------------------------------------------------------------------- announcement */
body.theme-voyage .announcement {
  background: var(--color-ink); color: var(--color-surface);
  text-align: center; padding: 0.5rem 1rem; font-size: 0.78rem; letter-spacing: 0.06em;
}

/* ------------------------------------------------------------------- utility strip + header */
/* The strip and the header are one band, `.vy-chrome`, and every colour in it comes from two
   variables: `--vy-chrome-ink` for words and `--vy-chrome-ground` for what is under them. By
   default that is the palette's surface on its ink — a solid band, on every page. Over a
   photograph (see "The header over a photograph" below) the band goes translucent and, when
   the picture's tone is known, the pair is swapped for whichever reads on it. Written against
   `var(--color-surface)` directly, as it was, there is no second pair to swap to. */
body.theme-voyage .vy-chrome {
  --vy-chrome-ink: var(--color-surface);
  --vy-chrome-ground: var(--color-ink);
  position: relative; z-index: 6;
}
/* `header_tone` (Design → Header colour) repoints the same two variables to another pair from
   the palette — never a typed colour, so the header can never end up unreadable in one colour
   scheme: every option is a background role and the ink role guaranteed against it. The menu,
   the search box and the drop-down all read `--vy-chrome-ground`/`-ink` already, so nothing
   about them needs to change to follow it. */
body.theme-voyage[data-header-tone="light"] .vy-chrome {
  --vy-chrome-ink: var(--color-ink); --vy-chrome-ground: var(--color-surface);
}
body.theme-voyage[data-header-tone="soft"] .vy-chrome {
  --vy-chrome-ink: var(--color-ink); --vy-chrome-ground: var(--color-surface-raised);
}
body.theme-voyage[data-header-tone="accent"] .vy-chrome {
  --vy-chrome-ink: var(--color-accent-ink); --vy-chrome-ground: var(--color-accent);
}
body.theme-voyage .vy-utility {
  background: var(--vy-chrome-ground);
  color: var(--vy-chrome-ink); position: relative; z-index: 6;
}
body.theme-voyage .vy-utility-inner {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  flex-wrap: wrap; padding: 0.5rem 0; font-size: 0.8rem;
}
body.theme-voyage .vy-utility-contact { display: flex; gap: 1.5rem; flex-wrap: wrap; }
body.theme-voyage .vy-utility a { color: var(--vy-chrome-ink); display: inline-flex; gap: 0.4rem; align-items: center; }
body.theme-voyage .vy-utility a:hover { color: var(--vy-chrome-ink); text-decoration: underline; }
body.theme-voyage .vy-utility .icon { width: 0.95rem; height: 0.95rem; }
body.theme-voyage .vy-utility-social { display: flex; gap: 0.9rem; }
body.theme-voyage .vy-utility-end { display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap; }
/* The pickers sit on the ink band, so they are a translucent wash of it with surface ink — the
   search box's treatment — rather than commerce's page-surface box, which was a white rectangle
   on a dark strip. The arrow is drawn in `currentColor` on the form, so the group sets it. */
body.theme-voyage .vy-utility .header-switchers { display: flex; gap: 0.35rem; color: var(--vy-chrome-ink); }
/* The same picker is the last entry of the phone menu (`_menu_language.html`), on the same band. */
body.theme-voyage .site-nav .nav-language {
  color: var(--vy-chrome-ink);
  border-top-color: color-mix(in srgb, var(--vy-chrome-ink) 22%, transparent);
}
body.theme-voyage .vy-utility .currency-select,
body.theme-voyage .vy-utility .locale-select,
body.theme-voyage .site-nav .nav-language .locale-select {
  padding: 0.2rem 1.6rem 0.2rem 0.55rem;
  font-size: 0.78rem;
  border-color: color-mix(in srgb, var(--vy-chrome-ink) 40%, transparent);
  border-radius: var(--vy-radius);
  background: color-mix(in srgb, var(--vy-chrome-ink) 12%, transparent);
  color: var(--vy-chrome-ink);
}
body.theme-voyage .vy-utility .currency-select:focus-visible,
body.theme-voyage .vy-utility .locale-select:focus-visible,
body.theme-voyage .site-nav .nav-language .locale-select:focus-visible { outline-color: var(--vy-chrome-ink); }

body.theme-voyage .site-header {
  position: relative; z-index: 6;
  background: var(--vy-chrome-ground);
  border-bottom: 1px solid color-mix(in srgb, var(--vy-chrome-ink) 20%, transparent);
}
body.theme-voyage .header-inner {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 1.25rem; padding: 0.9rem 0;
}
/* "Brand left, on a solid bar". No `order` here: the brand used to be given `order: -1` while
   the base rules still say `.site-nav { grid-column: 1 }` and `.site-brand { grid-column: 2 }`,
   so the brand was placed first at column 2 and the nav, whose column 1 is then *behind* the
   auto-placement cursor, was started on a new row — the logo on top with the menu beneath it.
   On a phone the brand, the actions and the search row are placed by the shared phone bar
   (`commerce.css`, `.header-inner.phone-bar`), the same for both header styles; on a desktop
   every part names its own column *and* row (below, in the 721px block). No columns are stated
   for this style here: a rule with the attribute in it is (0,3,1) and would beat the phone
   bar's two tracks. */
body.theme-voyage[data-header-style="inline"] .site-brand { justify-self: start; }
/* Centred brand — the middle column of the three-up grid. */
body.theme-voyage .site-brand {
  grid-column: 2; justify-self: center;
  font-family: var(--font-heading, "Playfair Display", Georgia, serif);
  font-size: 1.5rem; font-weight: 600; letter-spacing: 0.02em; color: var(--vy-chrome-ink);
}
body.theme-voyage .site-brand img { max-height: calc(2.2rem * var(--logo-scale, 1)); width: auto; }
/* **The menu states its ink, not only its links.** Whatever is drawn in the menu that is not a
   link takes the menu's `color`: the arrow `nav_phone.js` adds to open a sub-menu on a phone
   (`color: inherit`), and the label of an entry with no page of its own. With the ink on `a`
   alone both inherited the page's ink, on a band whose ground *is* the page's ink — the arrow
   was black on black, and with it the only way to an entry's sub-menu (reported on a live shop:
   "the menu doesn't show the sub-menus"). `--vy-chrome-ink` follows `header_tone` and the
   photograph the header is over, so this is right on all of them. */
body.theme-voyage .site-nav { grid-column: 1; display: flex; gap: 1.5rem; align-items: center; color: var(--vy-chrome-ink); }
body.theme-voyage .site-nav a { font-size: 0.86rem; font-weight: 500; color: var(--vy-chrome-ink); }
/* On the ink chrome the accent measures 3.55:1 light and 1.66:1 dark, so the current nav
   item — the one you are standing on — was the least readable thing in the header. The
   state is a rule under the word now; the word keeps the guaranteed pair. */
body.theme-voyage .site-nav a:hover { color: var(--vy-chrome-ink); text-decoration: underline;
  text-underline-offset: 5px; }
body.theme-voyage .site-nav a[aria-current="page"] {
  color: var(--vy-chrome-ink); box-shadow: inset 0 -2px 0 var(--color-accent);
}
/* The drop-down, and here it is a **correctness** fix as much as a design one.
   Voyage's nav ink is `--color-surface` because the bar is the ink chrome band. Those same
   links are what a submenu is made of, so on `commerce.css`'s default panel — grounded in
   `--c-bg`, the page surface — they were near-white on near-white: **1.0:1, in both colour
   schemes**, five of the six links in the Shop panel invisible, and in dark mode the same thing
   with the values swapped. Precisely what this theme's search box did, for precisely this
   reason. `commerce.css` now sets the panel's ink itself so no theme can leak the bar's colour
   into it by accident, and voyage takes the other road on purpose: the panel is a piece of the
   band, not a card on the page, which is also what a booking site's menu looks like.
   The column head is the **same ink**, told apart by size and letter-spacing rather than by a
   dimmed mix or the accent — this theme's own note above records the accent measuring 3.55:1
   light and 1.66:1 dark on this ground, and a dimmed pair is a contrast figure nobody has
   measured (the page-builder tones learned that one). The hover tints `currentColor` rather
   than naming a colour, so it cannot be wrong in one mode. */
body.theme-voyage .site-nav .nav-submenu {
  top: 100%;
  padding: 0.5rem 0;
  gap: 0;
  border: 0;
  border-radius: 0 0 var(--c-radius-sm) var(--c-radius-sm);
  background: var(--vy-chrome-ground);
  box-shadow: var(--c-shadow);
}
body.theme-voyage .site-nav .nav-submenu a {
  font-size: 0.86rem; font-weight: 500; color: var(--vy-chrome-ink);
  padding: 0.5rem 1.15rem; border-radius: 0;
}
body.theme-voyage .site-nav .nav-submenu a:hover,
body.theme-voyage .site-nav .nav-submenu a:focus-visible {
  background: color-mix(in srgb, currentColor 12%, transparent);
  color: var(--vy-chrome-ink);
}
body.theme-voyage .site-nav .nav-submenu-mega {
  padding: 1.25rem 1.35rem 1.35rem;
  gap: 0.1rem 2.25rem;
}
body.theme-voyage .site-nav .nav-submenu-mega .nav-column-head {
  color: var(--vy-chrome-ink); font-size: 0.66rem; letter-spacing: 0.2em;
  padding-inline: 1.15rem; margin-bottom: 0.55rem;
}
/* The end of the header: the search box, then Saved, the basket and the sign-in button
   (`.vy-header-end` in the template says why it is this box and not `.header-actions`). */
body.theme-voyage .vy-header-end { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: 0.8rem; }
body.theme-voyage .header-actions { display: flex; align-items: center; gap: 0.8rem; }
/* Voyage's header is the hero's own dark band, so the field is a translucent wash *on that
   band* and its ink is `--color-surface`. That only works if the form paints no ground of its
   own: commerce's `--color-surface` box used to sit underneath the wash, which put near-white
   ink on near-white and measured 1.0:1 in both colour schemes — the typed query was invisible.
   The button follows the header's ink, not the page's. */
body.theme-voyage .site-search {
  max-width: 12rem;
  border-radius: var(--vy-radius);
  border: 1px solid color-mix(in srgb, var(--vy-chrome-ink) 40%, transparent);
  background: color-mix(in srgb, var(--vy-chrome-ink) 12%, transparent);
}
body.theme-voyage .site-search-input {
  padding: 0.45rem 0.2rem 0.45rem 0.9rem; font-size: 0.84rem; color: var(--vy-chrome-ink);
}
body.theme-voyage .site-search-input::placeholder { color: color-mix(in srgb, var(--vy-chrome-ink) 72%, transparent); }
body.theme-voyage .site-search-button { color: var(--vy-chrome-ink); padding-inline: 0.35rem 0.7rem; }
/* A hover keeps the ink and tints `currentColor` behind it. Mixing the ink towards the band's
   ground was fine on the solid band and 4.39:1 on the translucent one over a photograph. */
body.theme-voyage .site-search-button:hover { background: color-mix(in srgb, currentColor 12%, transparent); }
/* This theme centres its listing hero, so the results page's box centres with it. */
/* The results page's box is on the *page*, not in the header's dark band, so it takes the
   page's pair back — ink on surface. Every colour above is chosen for the band, and inheriting
   them here put a near-white query on a near-white ground: the header bug one page over. */
body.theme-voyage .site-search.site-search-page {
  margin-inline: auto;
  background: var(--color-surface);
  border-color: var(--color-border);
}
body.theme-voyage .site-search.site-search-page .site-search-input { color: var(--color-ink); }
body.theme-voyage .site-search.site-search-page .site-search-input::placeholder {
  color: var(--color-ink-muted);
}
body.theme-voyage .site-search.site-search-page .site-search-button { color: var(--color-ink-muted); }
body.theme-voyage .icon-btn {
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  width: 2.2rem; height: 2.2rem; color: var(--vy-chrome-ink);
}
body.theme-voyage .icon-btn:hover { background: color-mix(in srgb, currentColor 12%, transparent); border-radius: 999px; }
body.theme-voyage .icon-btn .icon { width: 1.2rem; height: 1.2rem; }
body.theme-voyage .saved-link { font-size: 0.84rem; font-weight: 500; color: var(--vy-chrome-ink); }
body.theme-voyage .saved-link:hover { color: var(--vy-chrome-ink); text-decoration: underline; }
body.theme-voyage .cart-count {
  position: absolute; top: -0.05rem; right: -0.2rem; min-width: 1.1rem; height: 1.1rem;
  display: grid; place-items: center; border-radius: 999px;
  background: var(--color-accent); color: var(--color-accent-ink); font-size: 0.66rem; font-weight: 700;
}
/* The outlined "Login" pill the archetype puts at the far right of the header. */
body.theme-voyage .vy-header-cta {
  padding: 0.45rem 1.15rem; border: 1px solid var(--vy-chrome-ink); border-radius: var(--vy-radius);
  color: var(--vy-chrome-ink); font-size: 0.82rem; font-weight: 600; white-space: nowrap;
}
body.theme-voyage .vy-header-cta:hover { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-ink); }
body.theme-voyage .nav-toggle-input { display: none; }
/* The "Menu" button that ends the search row on a phone, drawn as the search box beside it is:
   a wash of the band's ink, outlined in it. Its shape and its icon's size are the shared phone
   bar's. */
body.theme-voyage .nav-toggle {
  display: none; cursor: pointer; color: var(--vy-chrome-ink);
  border-color: color-mix(in srgb, var(--vy-chrome-ink) 40%, transparent);
  border-radius: var(--vy-radius);
  background: color-mix(in srgb, var(--vy-chrome-ink) 12%, transparent);
  font-weight: 600; letter-spacing: 0.02em;
}

/* -------------------------------------------------------------------------------- buttons */
body.theme-voyage .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  padding: 0.8rem 1.7rem; border-radius: var(--vy-radius);
  border: 1px solid var(--color-accent); background: var(--color-accent); color: var(--color-accent-ink);
  font: inherit; font-size: 0.84rem; font-weight: 600; letter-spacing: 0.04em;
  cursor: pointer; text-align: center;
}
body.theme-voyage .btn:hover { filter: brightness(0.92); }
body.theme-voyage .btn:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 2px; }
body.theme-voyage .btn:disabled, body.theme-voyage .btn[disabled] { opacity: 0.45; cursor: not-allowed; filter: none; }
body.theme-voyage .btn-sm { padding: 0.45rem 1rem; font-size: 0.78rem; }
body.theme-voyage .btn-outline, body.theme-voyage .btn-secondary {
  background: transparent; color: var(--color-ink); border-color: var(--color-border);
}
body.theme-voyage .btn-outline:hover, body.theme-voyage .btn-secondary:hover {
  background: var(--color-surface-raised); filter: none;
}
body.theme-voyage .btn-full { width: 100%; }
/* The round link button that floats at the right of a section heading. */
body.theme-voyage .vy-round-link {
  /* `place-items: center` only centres each grid item *within its own row* — with no explicit
     rows the label and the icon become two auto-sized implicit rows, and `align-content`'s
     default of `normal` behaves as `stretch` for intrinsically-sized tracks, so each row grows
     to fill half the circle: the label centres in the top half and the icon in the bottom half
     rather than the two sitting together in the middle. `place-content: center` stops the rows
     stretching, so they pack at their own height and that pair is centred as a group. */
  display: inline-grid; place-items: center; place-content: center; gap: 0.15rem;
  text-align: center;
  width: 6.5rem; height: 6.5rem; border-radius: 999px; flex: none;
  background: var(--color-accent); color: var(--color-accent-ink);
  font-size: 0.78rem; font-weight: 600;
}
body.theme-voyage .vy-round-link:hover { filter: brightness(0.92); }
body.theme-voyage .vy-round-link .icon { width: 1.4rem; height: 1.4rem; }

/* ----------------------------------------------------------------------------------- hero */
/* `padding-bottom` on the hero reserves the half of the search panel that hangs below it, so the
   overlap is real geometry rather than a negative margin fighting the next section's padding. */
body.theme-voyage .vy-hero { position: relative; padding-bottom: 4.5rem; }
body.theme-voyage .vy-hero-media {
  position: relative; min-height: clamp(24rem, 62vh, 38rem); overflow: hidden;
  background: var(--color-ink);
}
body.theme-voyage .vy-hero-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
body.theme-voyage .vy-hero-media::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--color-ink) 55%, transparent) 0%,
    color-mix(in srgb, var(--color-ink) 34%, transparent) 45%,
    color-mix(in srgb, var(--color-ink) 58%, transparent) 100%);
}
body.theme-voyage .vy-hero-copy {
  position: absolute; inset: 0; display: grid; align-content: center; justify-items: center;
  text-align: center; z-index: 2; padding: 2rem 1.5rem;
}
body.theme-voyage .vy-hero-copy h1 {
  font-size: clamp(2.2rem, 6vw, 4rem); color: var(--color-surface); max-width: 20ch;
}
body.theme-voyage .vy-hero-copy p { color: var(--color-surface); margin-top: 1rem; max-width: 48ch; }
/* The floating panel. */
body.theme-voyage .vy-searchbar {
  position: relative; z-index: 4; margin-top: -4.5rem;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr)) auto;
  align-items: stretch; background: var(--color-surface);
  border: 1px solid var(--color-border); box-shadow: var(--c-shadow);
}
body.theme-voyage .vy-searchbar-field {
  display: grid; gap: 0.25rem; padding: 1rem 1.25rem;
  border-right: 1px solid var(--color-border);
}
body.theme-voyage .vy-searchbar-field:last-of-type { border-right: 0; }
body.theme-voyage .vy-searchbar-label {
  font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--color-ink-muted); font-weight: 600;
}
body.theme-voyage .vy-searchbar-field input, body.theme-voyage .vy-searchbar-field select {
  border: 0; background: transparent; color: var(--color-ink); font: inherit; font-size: 0.92rem;
  padding: 0; width: 100%;
}
body.theme-voyage .vy-searchbar-field input::placeholder { color: var(--color-ink-muted); }
body.theme-voyage .vy-searchbar-field input:focus-visible,
body.theme-voyage .vy-searchbar-field select:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
body.theme-voyage .vy-searchbar-submit {
  padding: 1rem 1.75rem; border: 0; cursor: pointer;
  background: var(--color-accent); color: var(--color-accent-ink);
  font: inherit; font-size: 0.86rem; font-weight: 700; line-height: 1.3;
}
body.theme-voyage .vy-searchbar-submit:hover { filter: brightness(0.92); }

/* Section heading: eyebrow + serif line on the left, round link on the right. */
body.theme-voyage .section-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  flex-wrap: wrap; margin-bottom: 2.25rem;
}
body.theme-voyage .section-head h2 { font-size: clamp(1.7rem, 4vw, 2.75rem); }

/* Destination tiles: tall photographs with the name in the corner, no panel. */
body.theme-voyage .vy-places { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr)); gap: 1.5rem; }
body.theme-voyage .vy-place {
  position: relative; min-height: 20rem; overflow: hidden; display: grid; align-content: end;
  padding: 1.5rem; background: var(--color-ink);
}
body.theme-voyage .vy-place img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
body.theme-voyage .vy-place::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, color-mix(in srgb, var(--color-ink) 78%, transparent), transparent 58%);
}
body.theme-voyage .vy-place-name {
  position: relative; z-index: 2; color: var(--color-surface);
  font-family: var(--font-heading, "Playfair Display", Georgia, serif); font-size: 1.5rem;
}
body.theme-voyage .vy-place-count { position: relative; z-index: 2; color: var(--color-surface); font-size: 0.85rem; opacity: 0.86; }

/* The about band with the circular years badge overlapping the picture. */
body.theme-voyage .vy-about {
  display: grid; grid-template-columns: 1fr 1.15fr; gap: clamp(2rem, 5vw, 4rem); align-items: center;
}
body.theme-voyage .vy-about h2 { font-size: clamp(1.8rem, 4vw, 3rem); margin-bottom: 1.25rem; }
body.theme-voyage .vy-about p { color: var(--color-ink-muted); }
body.theme-voyage .vy-about p + p { margin-top: 1rem; }
body.theme-voyage .vy-about-media { position: relative; }
body.theme-voyage .vy-about-media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
body.theme-voyage .vy-about-media-empty {
  width: 100%; aspect-ratio: 4 / 3;
  background: radial-gradient(60% 60% at 55% 45%, color-mix(in srgb, var(--color-accent) 24%, var(--color-surface-raised)), var(--color-surface-raised));
}
body.theme-voyage .vy-badge {
  position: absolute; top: -1.75rem; left: -1.75rem; width: 7.5rem; height: 7.5rem;
  border-radius: 999px; display: grid; place-items: center; text-align: center;
  background: var(--color-surface); border: 1px solid var(--color-border); box-shadow: var(--c-shadow);
}
body.theme-voyage .vy-badge strong { font-family: var(--font-heading, "Playfair Display", Georgia, serif); font-size: 1.6rem; }
body.theme-voyage .vy-badge span { font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-ink-muted); }

/* ---------------------------------------------------------------------------- newsletter */
body.theme-voyage .newsletter {
  display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: center;
  padding: clamp(2rem, 4vw, 3rem); background: var(--color-surface-raised);
}
body.theme-voyage .newsletter h2 { font-size: 1.7rem; margin-bottom: 0.4rem; }
body.theme-voyage .newsletter p { color: var(--color-ink-muted); font-size: 0.92rem; }
body.theme-voyage .newsletter-form { display: flex; gap: 0; flex-wrap: wrap; }
body.theme-voyage .newsletter-form input {
  padding: 0.85rem 1.1rem; min-width: 15rem; border-radius: var(--vy-radius) 0 0 var(--vy-radius);
  border: 1px solid var(--color-border); border-right: 0;
  background: var(--color-surface); color: var(--color-ink); font: inherit;
}
body.theme-voyage .newsletter-form input::placeholder { color: var(--color-ink-muted); }
body.theme-voyage .newsletter-form .btn { border-radius: 0 var(--vy-radius) var(--vy-radius) 0; }

/* -------------------------------------------------------------------------------- listing */
body.theme-voyage .listing-hero {
  position: relative; background: var(--color-surface-raised);
  padding: clamp(2.5rem, 5vw, 4rem) 0; text-align: center;
}
body.theme-voyage .listing-hero h1 { font-size: clamp(2rem, 5vw, 3.25rem); }
body.theme-voyage .listing-hero .lead { margin: 0.9rem auto 0; }
body.theme-voyage .breadcrumbs {
  display: flex; gap: 0.45rem; flex-wrap: wrap; align-items: center; justify-content: center;
  font-size: 0.8rem; color: var(--color-ink-muted); margin-bottom: 0.85rem;
}
body.theme-voyage .breadcrumbs a:hover { color: var(--color-accent); }
body.theme-voyage .breadcrumbs [aria-current="page"] { color: var(--color-ink); }
body.theme-voyage .listing { padding-top: 2.5rem; }
body.theme-voyage .filter-chips { display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; margin-bottom: 2rem; }
body.theme-voyage .filter-chips a {
  padding: 0.45rem 1.15rem; border-radius: var(--vy-radius); font-size: 0.84rem; font-weight: 600;
  background: var(--color-surface-raised); color: var(--color-ink-muted);
}
body.theme-voyage .filter-chips a:hover { color: var(--color-ink); }
body.theme-voyage .filter-chips a[aria-current="true"] { background: var(--color-accent); color: var(--color-accent-ink); }
body.theme-voyage .listing-toolbar {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding-bottom: 0.9rem; margin-bottom: 1.75rem; border-bottom: 1px solid var(--color-border);
}
body.theme-voyage .listing-count { font-size: 0.85rem; color: var(--color-ink-muted); }
body.theme-voyage .sort-links { display: flex; gap: 0.9rem; align-items: center; font-size: 0.85rem; }
body.theme-voyage .sort-links span { color: var(--color-ink-muted); }
body.theme-voyage .sort-links a { color: var(--color-ink-muted); font-weight: 600; }
body.theme-voyage .sort-links a:hover { color: var(--color-ink); }
body.theme-voyage .sort-links a[aria-current="true"] { color: var(--color-accent); }

/* The card: the caption panel is inset and lifted, so it overlaps the bottom of the picture. */
/* Not a grid at all: full-width rows, the picture beside the words. A trip is chosen on what it
   *is* — how long, where from, what is included — and a card three hundred pixels wide has room
   for a photograph and a price and nothing that answers the question. This is the one listing in
   the set you read down rather than scan across. */
body.theme-voyage .product-grid { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
body.theme-voyage .product-card {
  /* Inside a built page's Accent or Inverted band, restate the pair this surface carries. */
  --pb-ink: var(--color-ink); --pb-muted: var(--color-ink-muted);
  position: relative; display: grid; grid-template-columns: var(--vy-pic, 22rem) minmax(0, 1fr);
  align-items: stretch; border: 1px solid var(--color-border); background: var(--color-surface);
}
body.theme-voyage .product-media {
  display: block; position: relative; aspect-ratio: 4 / 3; overflow: hidden;
  background: var(--color-surface-raised); height: 100%;
}
body.theme-voyage .product-media img { width: 100%; height: 100%; object-fit: cover; }
/* A card is a row here — a 22rem picture beside its text — which suits a listing of one per line
   and cannot fit a built page's 2, 3 or 4 columns: the picture alone is wider than the track, so
   the text ran 21px off the window at 1180px. In those grids the card stacks, the way it does on
   a phone, and the picture's height comes from its width. */
body.theme-voyage .builder-page .product-grid.pb-count-2 .product-card,
body.theme-voyage .builder-page .product-grid.pb-count-3 .product-card,
body.theme-voyage .builder-page .product-grid.pb-count-4 .product-card {
  grid-template-columns: minmax(0, 1fr);
}
body.theme-voyage .builder-page .product-grid.pb-count-2 .product-media,
body.theme-voyage .builder-page .product-grid.pb-count-3 .product-media,
body.theme-voyage .builder-page .product-grid.pb-count-4 .product-media {
  aspect-ratio: 16 / 9; height: auto; width: 100%;
  /* Not stretched: a card made taller by its neighbour's text hands the spare height to its
     picture, so one row of cards had pictures 152px to 174px tall. */
  align-self: start;
}
/* Product display: a trip is a row, and a row can be shorter. The picture column is the only thing
   that sets how tall one is, so compact and dense narrow it (the words' own sizes and padding come
   from `commerce.css`, which every design shares). Choosing "2 or more across" turns the card the
   way a built page's grid does: stacked, the picture's height coming from its width. */
body.theme-voyage[data-card-density="compact"] { --vy-pic: 13rem; }
body.theme-voyage[data-card-density="dense"] { --vy-pic: 9rem; }
body.theme-voyage[data-listing-columns]:not([data-listing-columns="auto"]) .product-grid:not(.builder-page *):not(.product-page *) .product-card {
  grid-template-columns: minmax(0, 1fr);
}
body.theme-voyage[data-listing-columns]:not([data-listing-columns="auto"]) .product-grid:not(.builder-page *):not(.product-page *) .product-media {
  aspect-ratio: 16 / 9; height: auto; width: 100%; align-self: start;
}
body.theme-voyage .product-media.pattern-a { background: linear-gradient(160deg, color-mix(in srgb, var(--color-accent) 20%, var(--color-surface-raised)), var(--color-surface-raised)); }
body.theme-voyage .product-media.pattern-b { background: var(--color-surface-raised); }
body.theme-voyage .product-media.pattern-c { background: linear-gradient(20deg, color-mix(in srgb, var(--color-accent) 12%, var(--color-surface-raised)), var(--color-surface)); }
body.theme-voyage .product-media.pattern-d { background: linear-gradient(200deg, var(--color-surface-raised), var(--color-surface)); }
body.theme-voyage .product-badge {
  position: absolute; top: 0.75rem; right: 0.75rem; z-index: 2; padding: 0.2rem 0.7rem;
  border-radius: var(--vy-radius); background: var(--color-accent); color: var(--color-accent-ink);
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
}
body.theme-voyage .product-badge-kind { background: var(--color-ink); color: var(--color-surface); }
body.theme-voyage .product-body {
  position: relative; z-index: 2; margin: 0; padding: 1.5rem 1.75rem;
  background: var(--color-surface); display: grid; gap: 0.45rem; align-content: center;
}
body.theme-voyage .product-body h3 { font-size: 1.4rem; }
body.theme-voyage .product-body h3 { font-size: 1.05rem; line-height: 1.3; }
body.theme-voyage .product-body h3 a:hover { color: var(--color-accent); }
body.theme-voyage .product-meta {
  font-size: 0.8rem; color: var(--color-ink-muted); display: flex; gap: 0.35rem; align-items: center;
}
body.theme-voyage .price {
  font-family: var(--font-heading, "Playfair Display", Georgia, serif); font-size: 1.1rem; font-weight: 600;
  color: var(--color-ink); display: flex; gap: 0.45rem; align-items: baseline; flex-wrap: wrap;
}
body.theme-voyage .price-from, body.theme-voyage .price-approx {
  font-size: 0.74rem; font-weight: 400; color: var(--color-ink-muted); font-family: var(--font-body, Inter, sans-serif);
}
body.theme-voyage .price-compare { font-size: 0.86rem; font-weight: 400; color: var(--color-ink-muted); }
body.theme-voyage .price-lg { font-size: clamp(1.6rem, 3vw, 2.2rem); }
body.theme-voyage .wishlist-btn {
  position: absolute; top: 0.7rem; left: 0.7rem; z-index: 3;
  width: 2.1rem; height: 2.1rem; border-radius: 999px; display: grid; place-items: center; cursor: pointer;
  background: var(--color-surface); border: 0; color: var(--color-ink); box-shadow: var(--c-shadow);
}
body.theme-voyage .wishlist-btn:hover { color: var(--color-accent); }
body.theme-voyage .wishlist-btn .icon { width: 1rem; height: 1rem; }
/* Sized by CSS, like every other icon here, not by the `<svg>`'s own `width`/`height` — those
   were the only thing holding it at 14px, and `.product-meta` is `display: flex` with no
   `flex-shrink: 0` guard, so the icon compressed below its intrinsic size whenever a card's
   place text was long enough to compete for room in the row. Different place-name lengths on
   different cards read as different icon sizes. */
body.theme-voyage .product-meta .icon { width: 0.875rem; height: 0.875rem; flex-shrink: 0; }

body.theme-voyage .pagination {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--color-border);
}
body.theme-voyage .pagination-status { font-size: 0.85rem; color: var(--color-ink-muted); }
body.theme-voyage .listing-empty, body.theme-voyage .empty {
  text-align: center; padding: clamp(3rem, 7vw, 4.5rem) 1.5rem; display: grid; gap: 0.85rem;
  justify-items: center; background: var(--color-surface-raised);
}
body.theme-voyage .listing-empty .icon { width: 2.5rem; height: 2.5rem; color: var(--color-ink-muted); }
body.theme-voyage .listing-empty h2, body.theme-voyage .empty h3 { font-size: 1.35rem; }
body.theme-voyage .listing-empty p, body.theme-voyage .empty p { color: var(--color-ink-muted); }

/* --------------------------------------------------------------------------- product page */
/* A full-bleed masthead of the first photograph with the title over it, then the booking rail
   on the right — the shape a trip page wants, and the reason this theme suits events and tours
   as well as it suits a hotel. */
body.theme-voyage .vy-masthead { position: relative; min-height: clamp(11rem, 22vh, 14rem); overflow: hidden; background: var(--color-ink); }
body.theme-voyage .vy-masthead img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
body.theme-voyage .vy-masthead::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, color-mix(in srgb, var(--color-ink) 76%, transparent), color-mix(in srgb, var(--color-ink) 26%, transparent));
}
body.theme-voyage .vy-masthead-copy { position: absolute; inset: 0; display: grid; align-content: end; z-index: 2; padding-bottom: 2rem; }
body.theme-voyage .vy-masthead-copy h1 { font-size: clamp(1.9rem, 4.5vw, 3.25rem); color: var(--color-surface); }
body.theme-voyage .vy-masthead-copy .breadcrumbs { color: var(--color-surface); justify-content: flex-start; }
body.theme-voyage .vy-masthead-copy .breadcrumbs [aria-current="page"] { color: var(--color-surface); }
body.theme-voyage .product-page { padding: 0 0 0; }
body.theme-voyage .product-layout {
  /* The reading column takes three-quarters: a trip page is mostly plan, plate and
     itinerary, and the booking rail only has to be findable. */
  display: grid; grid-template-columns: minmax(0, 2.6fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 3.5rem); align-items: start; padding-top: 2.25rem;
}
body.theme-voyage .product-gallery { display: grid; gap: 0.6rem; }
/* The masthead above already carries the photograph as a band, so this one is the product itself:
   whole, on a mat, at a height that leaves the booking rail beside it on the first screen. */
body.theme-voyage .gallery-main { position: relative; aspect-ratio: 3 / 2; overflow: hidden; background: var(--color-surface-raised); }
body.theme-voyage .gallery-main img { width: 100%; height: 100%; object-fit: contain; }
body.theme-voyage .gallery-placeholder { width: 100%; height: 100%; }
body.theme-voyage .gallery-thumbs { display: flex; gap: 0.5rem; flex-wrap: wrap; }
body.theme-voyage .gallery-thumb {
  width: 4.5rem; aspect-ratio: 3 / 2; padding: 0; overflow: hidden; cursor: pointer; border-radius: 0;
  border: 1px solid var(--color-border); background: var(--color-surface-raised);
}
body.theme-voyage .gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }
body.theme-voyage .gallery-thumb[aria-pressed="true"] { border-color: var(--color-accent); border-width: 2px; }
/* The booking rail: bordered, lifted, and sticky — the panel that has to be reachable from
   anywhere on a long page. */
body.theme-voyage .product-info {
  position: sticky; top: calc(var(--hr-shown, 0px) + 1.5rem); display: grid; gap: 1rem; padding: 1.5rem;
  background: var(--color-surface); border: 1px solid var(--color-border); box-shadow: var(--c-shadow);
}
/* The rail is the card: an event's own booking card inside it is not drawn as a second one
   (hide does the same), which also gave a trip's card back the height of a second padding. */
body.theme-voyage .product-info .evt-book { border: 0; padding: 0; background: transparent; box-shadow: none; }
/* The plan, the questions and the reviews belong in the reading column, under the plate, with
   the rail beside them — not below the whole row, where a trip's rail left three-quarters of
   the page empty until it ended. The rail spans both rows so it stays sticky alongside them. */
body.theme-voyage .product-layout { grid-template-rows: auto 1fr; }
body.theme-voyage .product-info { grid-column: 2; grid-row: 1 / span 2; }
body.theme-voyage .product-layout > .product-sections { grid-column: 1; grid-row: 2; }
@media (max-width: 960px) {
  body.theme-voyage .product-layout { grid-template-rows: none; }
  body.theme-voyage .product-info,
  body.theme-voyage .product-layout > .product-sections { grid-column: auto; grid-row: auto; }
}
body.theme-voyage .product-info h1 { font-size: clamp(1.4rem, 2.8vw, 1.9rem); }
body.theme-voyage .product-lead { color: var(--color-ink-muted); font-size: 0.92rem; }
body.theme-voyage .availability {
  display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.86rem; font-weight: 600;
  color: var(--color-ink);
}
body.theme-voyage .availability::before {
  content: ""; width: 0.5rem; height: 0.5rem; border-radius: 999px; background: var(--color-success); flex: none;
}
body.theme-voyage .availability.is-out { color: var(--color-ink-muted); }
body.theme-voyage .availability.is-out::before { background: var(--color-danger); }
body.theme-voyage .availability.is-low::before { background: var(--color-warning); }
body.theme-voyage .option-group { border: 0; padding: 0; margin: 0; display: grid; gap: 0.45rem; }
body.theme-voyage .option-group legend {
  padding: 0; font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--color-ink-muted); font-weight: 600; margin-bottom: 0.55rem;
}
body.theme-voyage .option-values { display: flex; gap: 0.4rem; flex-wrap: wrap; }
body.theme-voyage button.option-pill {
  padding: 0.45rem 1rem; border-radius: var(--vy-radius); cursor: pointer; font: inherit;
  font-size: 0.84rem; font-weight: 600;
  border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-ink);
}
body.theme-voyage button.option-pill:hover { border-color: var(--color-ink-muted); }
body.theme-voyage button.option-pill[aria-pressed="true"] { background: var(--color-accent); color: var(--color-accent-ink); border-color: var(--color-accent); }
body.theme-voyage button.option-pill.unavailable { opacity: 0.4; text-decoration: line-through; }
body.theme-voyage .buy-form { display: grid; gap: 0.6rem; margin-top: 0.35rem; }
body.theme-voyage .buy-form-booking { display: block; }
body.theme-voyage .qty {
  display: inline-flex; align-items: stretch; border: 1px solid var(--color-border);
  border-radius: var(--vy-radius); overflow: hidden; justify-self: start;
}
body.theme-voyage .qty button {
  width: 2.4rem; height: 2.7rem; border: 0; background: var(--color-surface-raised); color: var(--color-ink);
  font-size: 1rem; cursor: pointer;
}
body.theme-voyage .qty button:hover { background: var(--color-border); }
body.theme-voyage .qty input {
  width: 2.75rem; border: 0; text-align: center; font: inherit; font-weight: 600;
  background: var(--color-surface); color: var(--color-ink); -moz-appearance: textfield;
}
body.theme-voyage .qty input::-webkit-outer-spin-button,
body.theme-voyage .qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
body.theme-voyage .btn-buy, body.theme-voyage .btn-enquire { width: 100%; padding-block: 0.9rem; }
body.theme-voyage .product-rating a { color: var(--color-ink-muted); }
body.theme-voyage .product-rating a:hover { color: var(--color-accent); }

/* --------------------------------------------------------------------------------- footer */
/* `--vy-foot-ground`/`-ink` is the footer's own pair, the same shape as the header's
   `--vy-chrome-ground`/`-ink` — the default is the inverted band this design shipped with, and
   `footer_tone` (Design → Footer colour) repoints both to another guaranteed pair from the
   palette. Every rule below reads the pair rather than `--color-ink`/`-surface` directly, which
   is what lets one setting move the band, the wordmark, every link and the divider together. */
body.theme-voyage .site-footer {
  --vy-foot-ground: var(--color-ink); --vy-foot-ink: var(--color-surface);
  margin-top: var(--vy-gap); background: var(--vy-foot-ground); color: var(--vy-foot-ink);
  padding: clamp(2.75rem, 6vw, 4rem) 0 1.5rem;
}
body.theme-voyage[data-footer-tone="light"] .site-footer {
  --vy-foot-ground: var(--color-surface); --vy-foot-ink: var(--color-ink);
}
body.theme-voyage[data-footer-tone="soft"] .site-footer {
  --vy-foot-ground: var(--color-surface-raised); --vy-foot-ink: var(--color-ink);
}
body.theme-voyage[data-footer-tone="accent"] .site-footer {
  --vy-foot-ground: var(--color-accent); --vy-foot-ink: var(--color-accent-ink);
}
body.theme-voyage .footer-grid {
  display: grid; gap: 2.25rem; grid-template-columns: 1.6fr repeat(3, 1fr); margin-bottom: 2.5rem;
}
/* The accent is guaranteed against `--vy-foot-ground`, not against the ink — on the light
   palette's dark-band default the teal wordmark measured 3.55:1 on the plum footer, and the
   four column headings with it. A pair is a pair; the accent stays for hover, where it is a
   state rather than the only thing carrying the text. */
body.theme-voyage .footer-brand .site-brand {
  color: var(--vy-foot-ink); display: inline-block; margin-bottom: 0.75rem; font-size: 1.4rem;
}
body.theme-voyage .footer-brand p { color: color-mix(in srgb, var(--vy-foot-ink) 84%, var(--vy-foot-ground)); font-size: 0.9rem; max-width: 32ch; }
body.theme-voyage .footer-col { display: grid; gap: 0.5rem; align-content: start; }
body.theme-voyage .footer-col h3 {
  font-family: var(--font-heading, "Playfair Display", Georgia, serif);
  font-size: 1.05rem; color: var(--vy-foot-ink); margin-bottom: 0.35rem;
}
body.theme-voyage .footer-col a, body.theme-voyage .footer-col span {
  font-size: 0.89rem; color: color-mix(in srgb, var(--vy-foot-ink) 85%, var(--vy-foot-ground));
}
/* Not `color: var(--color-accent)`: `footer_tone="accent"` can point the footer's own ground
   at that colour, which would make a hovered/pressed link the same colour as what is behind it.
   `--vy-foot-ink` always follows whichever ground is in force, so opacity alone stays readable
   against dark, light, soft and accent alike — the pattern `.footer-nav a:hover` already uses. */
body.theme-voyage .footer-col a:hover { opacity: 1; }
body.theme-voyage .footer-nav { display: flex; flex-wrap: wrap; gap: 1.1rem; }
body.theme-voyage .footer-nav a { font-size: 0.84rem; color: color-mix(in srgb, var(--vy-foot-ink) 85%, var(--vy-foot-ground)); }
body.theme-voyage .footer-nav a:hover { opacity: 1; }
body.theme-voyage .footer-bottom {
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding-top: 1.5rem; border-top: 1px solid color-mix(in srgb, var(--vy-foot-ink) 22%, transparent);
  font-size: 0.82rem; color: color-mix(in srgb, var(--vy-foot-ink) 84%, var(--vy-foot-ground));
}
body.theme-voyage .powered-by a { color: var(--vy-foot-ink); text-decoration: underline; }

/* ------------------------------------------------------------------------- shared pages */
/* See the note in studio's stylesheet. */
body.theme-voyage .commerce-page { padding: clamp(2.5rem, 5vw, 4rem) 0; }
body.theme-voyage .page-head h1 { font-size: clamp(1.7rem, 4vw, 2.6rem); }
body.theme-voyage .card, body.theme-voyage .auth-card, body.theme-voyage .summary-card,
body.theme-voyage .order-panel, body.theme-voyage .address-card, body.theme-voyage .checkout-section {
  border-radius: var(--vy-radius); border: 1px solid var(--color-border); background: var(--color-surface);
}
body.theme-voyage .input, body.theme-voyage .form input, body.theme-voyage .form textarea,
body.theme-voyage .form select {
  border-radius: var(--vy-radius); border: 1px solid var(--color-border); background: var(--color-surface);
}
body.theme-voyage .field label, body.theme-voyage .qty-label {
  font-size: 0.74rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-ink-muted);
}
body.theme-voyage .cart-line { border-bottom: 1px solid var(--color-border); }
body.theme-voyage .cart-thumb { border-radius: 0; }
/* The current item used to be a filled `--color-accent` pill, which every other account menu
   left behind for the quiet inline-start rule in `commerce.css` — this theme keeps its own
   rounding on the row (below) rather than the shared rule's own radius, and nothing else: no
   fill, so the indicator reads the same way here as it does on every other theme. */
body.theme-voyage .account-nav a { border-radius: var(--vy-radius); }
body.theme-voyage .course-card, body.theme-voyage .evt-list-card { border-radius: var(--vy-radius); }
body.theme-voyage .empty-state { border-radius: var(--vy-radius); }
body.theme-voyage .product-section-title { font-family: var(--font-heading, "Playfair Display", Georgia, serif); }

/* ----------------------------------------------------------------------------- responsive */
@media (max-width: 960px) {
  body.theme-voyage .product-card { grid-template-columns: 1fr; }
  /* Stacked, the height comes from the width and not the other way round. The desktop rule's
     `height: 100%` fills the row beside the text; left on, a grid item with a ratio derives its
     *width* from that height, and a two-across card on a phone drew a 227px picture in a 161px
     track — 40px past the edge of the screen, on the built front page's product grid. */
  body.theme-voyage .product-media { aspect-ratio: 16 / 9; height: auto; width: 100%; align-self: start; }
  body.theme-voyage .vy-about { grid-template-columns: 1fr; }
  body.theme-voyage .vy-badge { top: -1.25rem; left: -0.75rem; width: 6rem; height: 6rem; }
  body.theme-voyage .product-layout { grid-template-columns: 1fr; }
  body.theme-voyage .product-info { position: static; }
  body.theme-voyage .footer-grid { grid-template-columns: 1fr 1fr; }
  body.theme-voyage .newsletter { grid-template-columns: 1fr; }
}
/* Between the phone layout and a wide screen the three-up header could not hold a real shop's
   header: with a four-entry menu and a language picker it ran 237px off an 800px window with the
   shop's name alone, "Sign in" breaking inside its own pill. The outer tracks may now be narrower
   than their content, the menu wraps within its column, and the actions may wrap as a last
   resort — the pickers are in the utility strip now, which is what keeps them on one row. */
@media (min-width: 721px) {
  body.theme-voyage .header-inner { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
  body.theme-voyage .site-nav { flex-wrap: wrap; row-gap: 0.4rem; min-width: 0; }
  body.theme-voyage .vy-header-end { flex-wrap: wrap; justify-content: flex-end; row-gap: 0.5rem; }
  body.theme-voyage .header-actions { display: contents; }
  /* A line wraps on each item's flex-basis, before anything is asked to shrink — so at 12rem
     the search box pushed "My account" onto a second row at 1024px with room to spare once the
     box gave a little. A small basis lets it fill the row up to its 12rem cap instead. */
  body.theme-voyage .site-search { flex: 1 1 8rem; min-width: 0; }
  /* "Brand left": logo, then the menu beside it, then the actions — one row, each part placed in
     its own column and row (the DOM order is nav, brand, actions). `body.theme-voyage[…]` is
     (0,3,x) and beats the three-up rule above. */
  body.theme-voyage[data-header-style="inline"] .header-inner {
    grid-template-columns: auto minmax(0, 1fr) auto;
  }
  body.theme-voyage[data-header-style="inline"] .site-brand { grid-column: 1; grid-row: 1; }
  body.theme-voyage[data-header-style="inline"] .site-nav { grid-column: 2; grid-row: 1; }
  body.theme-voyage[data-header-style="inline"] .vy-header-end { grid-column: 3; grid-row: 1; }
}
/* 720px, not 780px: this is the same breakpoint `commerce.css`'s `.nav-group`/`.nav-submenu`
   structural rule switches at (and every other theme's own drawer breakpoint), so the drawer
   opening and a parent menu item's dropdown becoming a static, always-open list happen together.
   Left at 780px, a shop viewed between 721–780px had the drawer open while an item with children
   ("Team Building") was still laid out by the desktop absolutely-positioned hover-dropdown rule —
   visibly misaligned against a plain link ("About") sitting beside it as an ordinary stacked
   block. Reported against the live shop. */
@media (max-width: 720px) {
  /* **Two rows, laid out by the shared phone bar** (`commerce.css`): the logo with Saved, the
     basket and the sign-in button, then the search ended by the Menu button. What is here is
     how voyage draws them. The wrapper that is the actions' flex row on a desktop is not a box
     on a phone, so the search row and the actions are each placed by that grid. */
  body.theme-voyage .vy-header-end { display: contents; }
  body.theme-voyage .header-inner { padding: 0.7rem 0 0.8rem; }
  body.theme-voyage .header-actions { gap: 0.6rem; }
  /* The logo gets a cap the brand track can always hold — scoped to `.site-header` so it does
     not shrink the footer logo, which already has the wider cap in `commerce.css` for a column
     of its own. The cap still answers `--logo-scale` (a flat 7rem ignored the merchant's own
     "Extra large" choice and drew it no bigger than "Standard"), just held to a phone-sized
     ceiling on both axes rather than the desktop ones above. */
  body.theme-voyage .site-header .site-brand img {
    max-height: min(calc(2.2rem * var(--logo-scale, 1)), 3rem);
    max-width: min(calc(7rem * var(--logo-scale, 1)), 42vw);
  }
  /* The utility strip (`vy-utility-inner`) is a `flex-wrap: wrap` row by default — right on a
     wide screen, where it never has to wrap — and on a phone it is contact details, a language
     picker and social links fighting for one line, so it wrapped onto two or three and pushed
     the whole header down before a visitor had reached the menu. It scrolls sideways instead,
     the strip on every admin section already does this for the same reason. A social link's own
     name goes with it — an icon and a word for each of up to eight networks is most of the
     strip's width on its own — so only the glyph stays, and `.social-name` carries the name for
     whoever is using a screen reader rather than looking at the row. */
  body.theme-voyage .vy-utility-inner {
    flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
  body.theme-voyage .vy-utility-contact,
  body.theme-voyage .vy-utility-end { flex-wrap: nowrap; flex: none; }
  body.theme-voyage .vy-utility-social .social-name {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  /* The pickers are the last entry of the menu on a phone, so the strip's own are not drawn —
     and a strip with nothing else in it (no phone number or email shown, no social links) is
     not drawn either, rather than being an empty band above the logo. */
  body.theme-voyage .vy-utility .header-switchers { display: none; }
  body.theme-voyage .vy-utility:not(:has(.vy-utility-contact a)):not(:has(.vy-utility-social a)) { display: none; }
  /* The open menu: one column under a rule, in type a little larger than the desktop bar's.
     The column, and every entry being a full-width row a finger can press, are the phone
     bar's. */
  body.theme-voyage .site-nav {
    gap: 0; margin-top: 0.3rem; padding-top: 0.4rem;
    border-top: 1px solid color-mix(in srgb, var(--vy-chrome-ink) 22%, transparent);
  }
  body.theme-voyage .site-nav > a,
  body.theme-voyage .site-nav > .nav-group > a,
  body.theme-voyage .site-nav > .nav-group > .nav-group-label { font-size: 0.95rem; }
  body.theme-voyage .site-search .site-search-input { min-width: 0; width: 100%; }
  body.theme-voyage .vy-searchbar { grid-template-columns: 1fr; margin-top: -2.5rem; }
  body.theme-voyage .vy-searchbar-field { border-right: 0; border-bottom: 1px solid var(--color-border); }
  body.theme-voyage .vy-hero { padding-bottom: 2.5rem; }
  body.theme-voyage .footer-grid { grid-template-columns: 1fr; }
}


/* --------------------------------------------------------------- the built front page */
/* A new shop's `/` is a **page in the builder**, seeded from `layouts/home.json` — not this
   theme's `home.html`, which is only the fallback for a shop that hands `/` back. Those blocks
   are shared templates (`apps/builder/templates/builder/blocks/`) styled by `builder.css`, so a
   theme that styles none of them ships a front page identical to every other theme's with the
   palette swapped. That is exactly what happened: seven distinct `home.html` files, and the
   admin's theme gallery — which renders each card as a live iframe of this page — showed seven
   copies of one design.

   `body.theme-voyage .builder-page …` is (0,3,x) and beats `builder.css`'s (0,2,0). */
/* Full width, centred, serif, and tall enough to be a photograph — the travel opening. */
body.theme-voyage .builder-page .pb-hero { border-radius: 0; }
/* Only while nobody has chosen a Background: a chosen tone is painted at (0,3,0) and this rule
   is (0,3,1), so it used to win and leave the band the page's colour under the tone's ink. */
body.theme-voyage .builder-page .pb-hero:not([class*="pb-tone-"]) { background: transparent; }
body.theme-voyage .builder-page .pb-hero-body {
  padding: clamp(6rem, 15vw, 10rem) 0; display: grid; align-content: center; min-height: 70vh;
}
body.theme-voyage .builder-page .pb-hero-heading {
  font-family: var(--font-heading, "Playfair Display", Georgia, serif);
  font-size: clamp(2.4rem, 6vw, 4.2rem); font-weight: 500; max-width: 20ch;
}
/* Only when the block is *actually* centred. Forcing it here overrode `align` — the setting the
   merchant chose — and left the starter About page's heading at one indent and its own sentence
   at another, which is the bug `test_builder_page_layout_browser.py` exists for. */
body.theme-voyage .builder-page .pb-align-center .pb-hero-heading { margin-inline: auto; }
/* The search panel over the hero's bottom edge — this theme's whole shape, and until now it
   existed only in `home.html`, which a new shop does not render. `.vy-searchbar` there and this
   are the same design over two different mechanisms: that one is markup this theme owns, this
   one is the `date_search` block, which actually searches the shop's dates. The negative margin
   is what lifts it onto the picture above; `z-index` puts it over the carousel's own scrim. */
body.theme-voyage .builder-page .pb-date-search {
  position: relative; z-index: 4; margin-top: -4.5rem;
}
/* The panel is lifted onto the hero's bottom edge, which is exactly where the carousel keeps
   its dots and — once a merchant switches autoplay on — the pause button, now its own element
   in the same corner rather than nested inside the dots row. Covered, that button is a stop
   control nobody can reach, i.e. the WCAG 2.2.2 failure this theme would introduce on its own.
   So both move up above the panel. Unconditional rather than `:has(+ …)`: this theme's front
   page ships the panel under the banner, and a shop that removes it gets controls a little high
   on the picture, where the conditional version gets an unreachable control on any browser that
   does not support the selector. */
body.theme-voyage .builder-page .pb-carousel-dots,
body.theme-voyage .builder-page .pb-carousel > .pb-autoplay-toggle { bottom: 6rem; }
/* The slide's own words and button sit under the panel just the same. The body is centred, so a
   short slide hides the fault; a heading that wraps (a narrow window, or zoom) grows the slide
   and leaves the button 2rem from its edge, under a panel that covers the bottom 4.5rem. The
   slide reserves the overlap plus the dots' lane, inside its min-height, so nothing is taller. */
body.theme-voyage .builder-page .pb-slide { padding-bottom: 8.5rem; }
body.theme-voyage .builder-page .pb-date-search-head h2 {
  font-family: var(--font-heading, "Playfair Display", Georgia, serif);
}
body.theme-voyage .builder-page .pb-date-search .date-search {
  border-radius: 0; box-shadow: var(--c-shadow); align-items: end;
}
@media (max-width: 720px) {
  /* On a phone the panel is full width, so it no longer floats beside anything: without the
     bottom margin its edge sat directly on the stats band beneath it. */
  body.theme-voyage .builder-page .pb-date-search { margin-top: -2.5rem; margin-bottom: 1.5rem; }
  /* The lift above matches the panel's own overlap, which shrinks here from -4.5rem to -2.5rem
     — the same reason the dots and the pause button move up only as far as the phone actually
     needs. Left at 6rem there was a band of bare picture between the controls and the panel. */
  body.theme-voyage .builder-page .pb-carousel-dots,
  body.theme-voyage .builder-page .pb-carousel > .pb-autoplay-toggle { bottom: 3.25rem; }
  body.theme-voyage .builder-page .pb-slide { padding-bottom: 5.5rem; }
}
body.theme-voyage .builder-page .pb-cols-3,
body.theme-voyage .builder-page .pb-cols-4 { grid-template-columns: repeat(auto-fit, minmax(min(26rem, 100%), 1fr)); }
body.theme-voyage .builder-page .pb-heading {
  font-family: var(--font-heading, "Playfair Display", Georgia, serif);
}


/* ----------------------------------------------------------- the header over a photograph */
/* A clear header over the front page's own opening photograph: the fallback home hero
   (`.vy-under-chrome`, used by nothing else since the product masthead stopped claiming it) and
   the built front page when it opens with a carousel (`.builder-page.is-home`, `layout.is_home`
   — never just "a page that opens with a carousel", or a merchant's own About page would
   silently change its header the day they added one). `:has()` asks the page itself, so a
   merchant who moves the carousel down, or a flash message landing above the hero, gets the
   solid band back with nobody passing a flag; a browser without `:has()` gets it too.

   And the same treatment over `.page-banner` (`{% page_banner %}`, `apps/core/templatetags/
   banner_tags.py`) — a built page's own header image, or the shop's default, on the pages that
   have one. It is a plain `<img>` behind a first-class element rather than a block inside the
   builder, so its own `:has()` twin needs no `.pb-carousel`-shaped path through the wrapper: just
   `#main > .page-banner:first-child`. The merchant's own choice, decided in plan mode — a header
   image gets the same clear-over-a-photograph header a hero does, on the reasoning that the two
   are the same picture-behind-the-nav shape and a second, solid-header treatment for one of them
   would be a second thing to explain.

   Every carousel path below has an extra `> :first-child >` between the section and
   `.pb-carousel`: the builder wraps each block in its own `<div data-block="…">` placeholder and
   renders the block's markup *inside* it, so `.pb-carousel` is the placeholder's child, not the
   section's — one level deeper than a selector written against the block's own template would
   expect. Skipping that level meant `:has()` never matched on a real page and the header stayed
   solid over every carousel on the platform.

   The header itself is fully transparent — no wash of its own — and legibility comes from a
   separate fade behind the words (`.vy-chrome::before`), darkest at the very top and thinning
   towards the header's own bottom edge, so the photograph is genuinely visible through most of
   the band rather than washed pale or dark across the whole of it. It does not reach zero by
   that edge: `.vy-header-cta`, the "Login" pill, sits in the *second* row of the band — an
   `.vy-utility` strip of contact links and pickers sits above `.site-header`'s own nav — so a
   fade tuned to be gone by the header's bottom leaves its weakest reading exactly where a
   control has to be pressed. `test_the_words_on_the_band_read_against_what_is_actually_behind_them`
   measures real pixels behind real words and is what found this; 82% (the design's default,
   `--header-overlay`, `theme.SHARED_SETTINGS`'s `header_overlay`) is what that test passed at.
   A merchant whose own photograph reads differently is not stuck with it — the 160% stop is
   left as the design's own, since it is the shape of the fade rather than its strength.
   `data-photo-tone` still decides which pair of
   `--vy-chrome-ground`/`-ink` the fade and the words are drawn in, because "light photograph" is
   absolute and the palette's ink is not; with no tone at all (a mixed or unreadable picture, or
   none) the ink/ground pair is left at its ordinary solid-header value, which is the safe
   default the picture's own scrim was already written to read against.
   "Light" and "dark" are absolute, while the palette's ink is light in one scheme and dark in
   the other, so the pair is chosen with `light-dark()`, which follows the page's
   `color-scheme` (`base.html`'s `<meta>`: the merchant's setting, then the device). With a
   known tone the picture's scrim fades in below the band, or it would darken the ground under
   dark ink. The thresholds are in `apps/core/photo_tone.py`, and
   `test_voyage_photo_chrome_browser.py` measures the result and that `--vy-chrome-room` —
   how much taller the picture grows so its own words clear the band — is at least the band's
   height at every width. */
body.theme-voyage { --vy-chrome-room: 7rem; }
/* The band is `4.3rem + 2.2rem × the logo's scale` tall (the utility strip, the header's padding,
   then the logo), so a merchant's "Logo size in the header" changes how much room the picture
   has to make. It was one fixed 7rem, which is right up to *Standard* and 24px short at
   *Extra large*: the slide's own words started under the band and its eyebrow rested 8px below
   it. `--logo-scale` lives on `.site-header`, where this variable cannot read it, so the size
   is read from the attribute it comes from. A phone's 11rem already clears every size, since
   the phone logo is capped at 3rem. */
@media (min-width: 781px) {
  body.theme-voyage[data-logo-size="large"] { --vy-chrome-room: 8rem; }
  body.theme-voyage[data-logo-size="x-large"] { --vy-chrome-room: 9rem; }
  body.theme-voyage[data-logo-size="xx-large"] { --vy-chrome-room: 10.5rem; }
}
@media (max-width: 780px) {
  body.theme-voyage { --vy-chrome-room: 11rem; }
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-chrome,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) .vy-chrome,
body.theme-voyage:has(#main > .page-banner:first-child) .vy-chrome {
  position: absolute; inset-inline: 0; background: transparent;
}
/* The fade sits behind the words and above the photograph — a stripe of readability rather
   than a wash over the whole band. */
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-chrome::before,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) .vy-chrome::before,
body.theme-voyage:has(#main > .page-banner:first-child) .vy-chrome::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--vy-chrome-ground) var(--header-overlay, 82%), transparent) 0%, transparent 160%);
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-utility,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) .vy-utility,
body.theme-voyage:has(#main > .page-banner:first-child) .vy-utility,
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .site-header,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) .site-header,
body.theme-voyage:has(#main > .page-banner:first-child) .site-header {
  background: transparent;
}
/* The search field's placeholder is the band's ink at 72%, which does not read over the light
   half of a two-tone photograph — so over a picture the field lays down a little of the band's
   own ground under its words, independent of the header fade above it. The phone's "Menu"
   button ends the same row, the lowest in the band and so where the fade is thinnest, and is
   drawn as the field's pair: it gets the same ground. */
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-chrome .site-search,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) .vy-chrome .site-search,
body.theme-voyage:has(#main > .page-banner:first-child) .vy-chrome .site-search,
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-chrome .phone-bar-row .nav-toggle,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) .vy-chrome .phone-bar-row .nav-toggle,
body.theme-voyage:has(#main > .page-banner:first-child) .vy-chrome .phone-bar-row .nav-toggle {
  background: color-mix(in srgb, var(--vy-chrome-ground) 55%, transparent);
}
/* An open phone menu is a column of links running down over the picture: solid, not a wash. */
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .site-header:has(.nav-toggle-input:checked),
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) .site-header:has(.nav-toggle-input:checked),
body.theme-voyage:has(#main > .page-banner:first-child) .site-header:has(.nav-toggle-input:checked) {
  background: var(--vy-chrome-ground);
}

body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone="light"]) .vy-chrome,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child[data-photo-tone="light"]) .vy-chrome,
body.theme-voyage:has(#main > .page-banner:first-child[data-photo-tone="light"]) .vy-chrome {
  --vy-chrome-ink: light-dark(var(--color-ink), var(--color-surface));
  --vy-chrome-ground: light-dark(var(--color-surface), var(--color-ink));
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone="dark"]) .vy-chrome,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child[data-photo-tone="dark"]) .vy-chrome,
body.theme-voyage:has(#main > .page-banner:first-child[data-photo-tone="dark"]) .vy-chrome {
  --vy-chrome-ink: light-dark(var(--color-surface), var(--color-ink));
  --vy-chrome-ground: light-dark(var(--color-ink), var(--color-surface));
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone]) .vy-masthead::after,
body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone]) .vy-hero-media::after,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) #main > .builder-page > :first-child > :first-child > .pb-carousel .pb-slide[data-photo-tone] .pb-slide-scrim {
  /* **A slide's mask is its own slide's**, keyed on the slide's tone and not on the carousel
     root's. The root's tone follows whichever slide is in front, and a slide with no tone (a
     mixed photograph) removes it — so a mask keyed on the root switched on and off with the
     header as the strip moved, and the second slide's top edge darkened in one step the moment
     the strip settled. Carried by the slide, it travels with its own picture. */
  /* Faded in below the band rather than cut off at it: a hard `top` left a stripe of bare
     photograph between the band and the scrim, as wide as the room is taller than the band. */
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(var(--vy-chrome-room) - 1rem), black calc(var(--vy-chrome-room) + 5rem));
  mask-image: linear-gradient(to bottom, transparent calc(var(--vy-chrome-room) - 1rem), black calc(var(--vy-chrome-room) + 5rem));
}
/* The logo made for the ground the picture gives it — the same decision as the ink above. */
body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone="light"]) .vy-chrome .site-brand .brand-logo-light,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child[data-photo-tone="light"]) .vy-chrome .site-brand .brand-logo-light,
body.theme-voyage:has(#main > .page-banner:first-child[data-photo-tone="light"]) .vy-chrome .site-brand .brand-logo-light,
body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone="dark"]) .vy-chrome .site-brand .brand-logo-dark,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child[data-photo-tone="dark"]) .vy-chrome .site-brand .brand-logo-dark,
body.theme-voyage:has(#main > .page-banner:first-child[data-photo-tone="dark"]) .vy-chrome .site-brand .brand-logo-dark {
  display: inline-block;
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone="light"]) .vy-chrome .site-brand .brand-logo-dark,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child[data-photo-tone="light"]) .vy-chrome .site-brand .brand-logo-dark,
body.theme-voyage:has(#main > .page-banner:first-child[data-photo-tone="light"]) .vy-chrome .site-brand .brand-logo-dark,
body.theme-voyage:has(#main > .vy-under-chrome:first-child[data-photo-tone="dark"]) .vy-chrome .site-brand .brand-logo-light,
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child[data-photo-tone="dark"]) .vy-chrome .site-brand .brand-logo-light,
body.theme-voyage:has(#main > .page-banner:first-child[data-photo-tone="dark"]) .vy-chrome .site-brand .brand-logo-light {
  display: none;
}


/* Room for the band, so the picture's own words start below it. */
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-masthead {
  min-height: calc(clamp(11rem, 22vh, 14rem) + var(--vy-chrome-room));
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-masthead-copy {
  padding-top: var(--vy-chrome-room);
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-hero-media {
  min-height: calc(clamp(24rem, 62vh, 38rem) + var(--vy-chrome-room));
}
body.theme-voyage:has(#main > .vy-under-chrome:first-child) .vy-hero-copy {
  padding-top: calc(2rem + var(--vy-chrome-room));
}
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) #main > .builder-page > :first-child > :first-child > .pb-carousel .pb-slide {
  padding-top: var(--vy-chrome-room);
}
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) #main > .builder-page > :first-child > :first-child > .pb-carousel-band .pb-slide {
  min-height: calc(clamp(14rem, 26vh, 20rem) + var(--vy-chrome-room));
}
body.theme-voyage:has(#main > .builder-page.is-home:first-child > :first-child > :first-child > .pb-carousel:first-child) #main > .builder-page > :first-child > :first-child > .pb-carousel-tall .pb-slide {
  /* `builder.css`'s shared `tall` clamp is 52vh — right for a carousel with no chrome floating
     over it. Adding the band's own room on top of that unchanged clamp made voyage's tall
     carousel as vertically tall as studio's full-`screen` one (measured: both landed at 0.75 of
     the viewport at 1440x900, the exact clash `test_layout_architecture_browser.py` exists to
     catch), the moment the room was actually being reserved rather than never applying. Voyage's
     archetype is a poster over the lower edge, not a full screen, so the clamp is voyage's own,
     narrower one — the band's height comes out of the slide's own budget instead of on top of it. */
  min-height: calc(clamp(20rem, 44vh, 30rem) + var(--vy-chrome-room));
}
/* `.page-banner` needs no `:has()` here — it is the element itself, not a sibling reached
   through one — so its own room is just `min-height` on top of `commerce.css`'s `height`, which
   loses to `min-height` whenever the clamp alone would be shorter than the band. */
body.theme-voyage .page-banner {
  min-height: calc(clamp(10rem, 24vw, 18rem) + var(--vy-chrome-room));
}


/* ------------------------------------------------------------------------------- motion */
/* A drift upward and forward, as if the page were arriving.
   The mechanism is `commerce.css`'s — hidden only behind `html.js-reveal`, honouring
   `prefers-reduced-motion` — and a theme sets only its character. */
body.theme-voyage {
  /* a landscape rewards a slow push in */
  --hover-zoom: 1.06;
  --reveal-shift: 26px;
  --reveal-scale: 1.02;
  --reveal-duration: 640ms;
  --reveal-ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* -------------------------------------------------------------------------------- headroom */
/* When the band is pinned (`static/js/headroom.js`) it is over the page's own text, not over the
   opening photograph, so it takes the solid ground its palette pair already defines — including
   the photo-tone pair, which is an ink and a ground made for each other whatever the photograph.
   The fade and the strip of contact links belong to the top of a page: a wash over a paragraph
   is unreadable, and the strip would double the height of a bar that comes back on a scroll up. */
body.theme-voyage .vy-chrome { --hr-ground: var(--vy-chrome-ground); }
body.theme-voyage .vy-chrome.hr-fixed::before { display: none !important; }
body.theme-voyage .vy-chrome.hr-fixed .vy-utility { display: none; }

/* ---- the month calendar (`partials/_month_calendar.html`, shared; this is only its voice) ---- */
/* Travel booking: the panel floats over the page like the search panel does. */
body.theme-voyage .mcal-panel { box-shadow: 0 22px 44px -22px color-mix(in srgb, var(--c-text) 45%, transparent); }
body.theme-voyage .mcal-caption { font-family: var(--font-heading); }

/* The tour facts line under a card's name: grade, youngest age, duration (`{% card_facts %}`). */
body.theme-voyage .product-card .product-facts {
  margin: 0.4rem 0 0; font-size: 0.9rem; color: var(--color-ink);
}
