/* ==========================================================================
   Ocean View — main stylesheet
   Hand-written. No frameworks, no builders.
   Order: tokens > base > utilities > layout > components > sections > responsive
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */
:root {
  /* Ink — the marketing palette is warm violet-black, not ocean blue. */
  --ov-ink:         #1b1035;
  --ov-ink-deep:    #130b26;
  --ov-body:        rgba(27, 16, 53, .72);
  --ov-body-soft:   rgba(27, 16, 53, .62);
  --ov-muted:       rgba(27, 16, 53, .45);
  --ov-quote:       rgba(27, 16, 53, .82);
  --ov-lead:        rgba(27, 16, 53, .62);

  /* Surfaces */
  --ov-bg:          #f7f5ff;
  --ov-surface:     #ffffff;
  --ov-rule:        rgba(91, 49, 214, .16);

  /* Brand.
     Violet is the brand, coral is the accent that marks the one thing on a
     screen worth looking at first. They are never used at equal weight. */
  --ov-violet:      #5b31d6;
  --ov-violet-mid:  #6d43e0;
  --ov-violet-deep: #3a2bd6;
  --ov-violet-soft: #8b5cf6;
  --ov-violet-pale: #c4b5fd;
  --ov-coral:       #ff7a6b;
  --ov-blue:        #3b82f6;
  --ov-blue-soft:   #5b8cf0;
  --ov-link:        #5b31d6;

  /* Kept so the Travel page's sections still resolve every variable they use.
     They now read as tints of the brand rather than as a second palette. */
  --ov-teal:        #5b31d6;
  --ov-teal-deep:   #4a27b8;
  --ov-teal-dark:   #3a2bd6;

  /* Card backgrounds (solid layer under the glass) */
  --ov-tint-blue:   #f4f6ff;
  --ov-tint-mint:   #fff5f3;
  --ov-tint-violet: #f3efff;
  --ov-tint-violet-strong: #efe9ff;

  /* Glass */
  --ov-glass:       rgba(255, 255, 255, .58);
  --ov-glass-soft:  rgba(255, 255, 255, .5);
  --ov-glass-strong:rgba(255, 255, 255, .72);
  --ov-glass-brd:   rgba(255, 255, 255, .9);
  --ov-glass-brd-2: rgba(255, 255, 255, .95);
  --ov-blur:        blur(20px);

  /* Shadows — violet-tinted, so a raised card reads as lifted off the lavender
     ground rather than as a grey smudge sitting on it. */
  --ov-sh-sm:  0 10px 24px rgba(91, 49, 214, .10);
  --ov-sh-md:  0 14px 32px rgba(91, 49, 214, .11);
  --ov-sh-lg:  0 18px 40px rgba(91, 49, 214, .13);
  --ov-sh-xl:  0 22px 46px rgba(91, 49, 214, .18);
  --ov-sh-2xl: 0 30px 62px rgba(91, 49, 214, .20);
  --ov-sh-violet: 0 22px 44px -20px rgba(91, 49, 214, .7);
  --ov-sh-hover:  0 34px 64px rgba(91, 49, 214, .22);

  /* Radii */
  --ov-r-sm: 13px;
  --ov-r-md: 18px;
  --ov-r-lg: 22px;
  --ov-r-xl: 26px;
  --ov-r-2xl: 28px;
  --ov-r-pill: 999px;

  /* Typography */
  --ov-font-display:  'Bricolage Grotesque', 'Trebuchet MS', system-ui, sans-serif;
  --ov-font-body:     'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --ov-font-wordmark: 'Jost', 'Futura', system-ui, sans-serif;

  /* Layout */
  --ov-max:      1180px;
  /* Held back from the edges. Fully fluid it read as a page with no frame; a
     cap this size still fills a laptop screen but gives a wide monitor a
     margin to sit the layout in. */
  --ov-max-wide: min(1640px, 100%);
  --ov-gutter:   24px;
  --ov-section-y: clamp(44px, 6vw, 88px);

  --ov-ease: cubic-bezier(.2, .75, .2, 1);
}

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ov-bg);
  color: var(--ov-ink);
  font-family: var(--ov-font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

a {
  color: var(--ov-link);
  text-decoration: none;
  transition: color .2s var(--ov-ease);
}
a:hover { color: var(--ov-ink); }

:focus-visible {
  outline: 3px solid var(--ov-violet);
  outline-offset: 3px;
  border-radius: 8px;
}

h1, h2, h3, h4 {
  font-family: var(--ov-font-display);
  color: var(--ov-ink);
  letter-spacing: -.03em;
  margin: 0;
}

p { margin: 0; }

button { font-family: inherit; }

summary { cursor: pointer; list-style: none; }
summary::-webkit-details-marker { display: none; }

/* Skip link (accessibility) */
.ov-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  background: #fff;
  color: var(--ov-ink);
  padding: 12px 18px;
  border-radius: 0 0 12px 0;
  font-size: 14px;
  font-weight: 600;
}
.ov-skip:focus { left: 0; }

.ov-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   3. Page wrapper (the page gradient)
   -------------------------------------------------------------------------- */
.ov-page {
  max-width: 100%;
  overflow-x: clip;
  background: linear-gradient(180deg,
    #eaf6ff 0%,
    #d3e9fa 26%,
    #e4f3f1 58%,
    #eeeafb 82%,
    #f6f8ff 100%);
}

.ov-wrap {
  max-width: var(--ov-max);
  margin-inline: auto;
  padding-inline: var(--ov-gutter);
}

.ov-section { padding-block: var(--ov-section-y); }

/* Scroll progress bar (injected by main.js) */
.ov-progress {
  position: fixed;
  left: 0; top: 0;
  height: 3px;
  width: 0;
  z-index: 99;
  /* The marketing palette, not the travel one. This still carried a hard-coded
     ocean blue from before the pivot — the only blue left on the page that
     answered to nothing in the theme. */
  background: linear-gradient(90deg, var(--ov-violet-soft), var(--ov-violet) 58%, var(--ov-coral));
  transition: width .12s linear;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   4. Section typography
   -------------------------------------------------------------------------- */
.ov-eyebrow {
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ov-teal-deep);
  margin-bottom: 10px;
}

.ov-h1 {
  font-weight: 800;
  font-size: clamp(31px, 3.9vw, 50px);
  line-height: 1.02;
  letter-spacing: -.036em;
  color: var(--ov-ink-deep);
  text-wrap: balance;
  overflow-wrap: anywhere;
}
.ov-h1 em { font-style: normal; color: var(--ov-violet-deep); }

.ov-h2 {
  font-weight: 800;
  font-size: clamp(26px, 3.6vw, 42px);
  line-height: 1.06;
}

.ov-h3 {
  font-weight: 600;
  font-size: 17.5px;
  letter-spacing: -.01em;
}

.ov-lead {
  font-size: 15.5px;
  line-height: 1.68;
  color: var(--ov-body);
  max-width: 56ch;
}

.ov-text { font-size: 14px; line-height: 1.6; color: var(--ov-body); }

/* --------------------------------------------------------------------------
   5. Glass and cards
   -------------------------------------------------------------------------- */
/* .ov-card = clipped container with a shadow; holds .ov-card__tint and .ov-card__body */
.ov-card {
  position: relative;
  border-radius: var(--ov-r-lg);
  overflow: hidden;
  box-shadow: var(--ov-sh-lg);
  transition: transform .45s var(--ov-ease), box-shadow .45s var(--ov-ease);
}

.ov-card__tint {
  position: absolute;
  inset: 0;
  background: var(--ov-tint-blue);
}

.ov-card__body {
  position: relative;
  background: var(--ov-glass);
  -webkit-backdrop-filter: var(--ov-blur);
  backdrop-filter: var(--ov-blur);
  border: 1px solid var(--ov-glass-brd);
  border-radius: inherit;
  padding: 22px;
}

/* Tint variants */
.ov-card--mint   .ov-card__tint { background: var(--ov-tint-mint); }
.ov-card--violet .ov-card__tint { background: var(--ov-tint-violet); }
.ov-card--violet { box-shadow: var(--ov-sh-violet); }
.ov-card--violet .ov-card__body { background: var(--ov-glass-soft); }
.ov-card--violet-strong { box-shadow: 0 20px 44px rgba(91, 63, 214, .18); }
/* Lift on hover (fine-pointer devices only) */
@media (hover: hover) and (pointer: fine) {
  .ov-lift:hover {
    transform: translateY(-6px);
    box-shadow: var(--ov-sh-hover);
  }
}

/* Placeholder shown wherever no image has been uploaded yet.
   Textured rather than a flat gradient, so an empty card reads as a
   deliberate design surface instead of a broken image. */
.ov-imgslot {
  display: block;
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 18% 0%, rgba(255, 255, 255, .6), transparent 58%),
    linear-gradient(140deg, #d6ecff 0%, #dee7ff 46%, #e7e0ff 100%);
}
/* Soft horizon curve */
.ov-imgslot::before {
  content: "";
  position: absolute;
  left: -20%; right: -20%; bottom: -42%;
  height: 78%;
  border-radius: 50% 50% 0 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, .5), rgba(255, 255, 255, 0));
}
/* Fine dot texture */
.ov-imgslot::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, rgba(255, 255, 255, .55) 1px, transparent 1px);
  background-size: 16px 16px;
  opacity: .45;
}

/* --------------------------------------------------------------------------
   6. Buttons and pills
   -------------------------------------------------------------------------- */
.ov-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: var(--ov-font-body);
  font-size: 15px;
  font-weight: 600;
  padding: 14px 22px;
  border-radius: var(--ov-r-pill);
  border: 0;
  cursor: pointer;
  text-align: center;
  white-space: nowrap;
  transition: transform .45s var(--ov-ease), box-shadow .45s var(--ov-ease), background-color .2s;
}

.ov-btn--primary {
  position: relative;
  overflow: hidden;
  background: var(--ov-violet);
  color: #fff;
  box-shadow: 0 18px 34px rgba(30, 14, 90, .36);
}
.ov-btn--primary:hover { color: #fff; background: var(--ov-violet-mid); }

/* Sheen sweeping across the primary button */
.ov-btn--primary .ov-sheen {
  position: absolute;
  top: 0; bottom: 0;
  width: 38%;
  background: linear-gradient(100deg,
    rgba(255,255,255,0), rgba(255,255,255,.45), rgba(255,255,255,0));
  animation: ov-sheen 3.8s ease-in-out infinite;
  pointer-events: none;
}

.ov-btn--ghost {
  background: rgba(255, 255, 255, .78);
  border: 1px solid var(--ov-glass-brd-2);
  color: var(--ov-ink);
  padding: 12px 18px;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}
.ov-btn--ghost:hover { color: var(--ov-ink); background: #fff; }

.ov-btn--sm { font-size: 14px; padding: 12px 18px; box-shadow: var(--ov-sh-sm); }

/* Square button for the search bar and the form */
.ov-btn--field {
  border-radius: var(--ov-r-sm);
  padding: 13px 22px;
  min-height: 48px;
  background: var(--ov-violet);
  color: #fff;
  box-shadow: 0 14px 26px rgba(91, 63, 214, .28);
}
.ov-btn--field:hover { background: var(--ov-violet-mid); }

.ov-pill {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 11.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ov-teal-dark);
  background: rgba(255, 255, 255, .72);
  border: 1px solid var(--ov-glass-brd-2);
  border-radius: var(--ov-r-pill);
  padding: 8px 13px;
}
.ov-pill__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ov-teal);
  flex: none;
}

/* --------------------------------------------------------------------------
   7. Form fields
   -------------------------------------------------------------------------- */
.ov-field {
  position: relative;
  display: grid;
  grid-template-columns: 22px 1fr;
  grid-template-areas:
    "icon label"
    "icon input";
  align-items: center;
  column-gap: 10px;
  min-width: 0;
  padding: 9px 14px;
  border: 1px solid rgba(17, 68, 112, .14);
  border-radius: var(--ov-r-sm);
  background: #fff;
  font-size: 12px;
  color: var(--ov-muted);
  transition: border-color .2s var(--ov-ease), box-shadow .2s var(--ov-ease);
}
.ov-field:focus-within { border-color: var(--ov-violet); box-shadow: 0 0 0 3px rgba(91, 63, 214, .14); }
.ov-field > .ov-icon { grid-area: icon; width: 20px; height: 20px; color: var(--ov-violet); opacity: .8; }
.ov-field__label { grid-area: label; font-size: 11.5px; line-height: 1.2; }
.ov-field--lg { font-size: 12.5px; }

/* The icon column is reserved by the shared field layout, but the two contact
   fields have no icon. That gutter used to be hidden by the label being
   misplaced into it; with the label where it belongs, the gap shows. Collapse
   the column when there is nothing to put in it. Falls back to the reserved
   column where :has() is unsupported, which is what it looked like before. */
.ov-field:not(:has(> .ov-icon)) {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "label"
    "input";
}

.ov-field input,
.ov-field select,
.ov-field textarea {
  grid-area: input;
  font-family: var(--ov-font-body);
  font-size: 14.5px;
  font-weight: 500;
  color: var(--ov-ink);
  background: none;
  border: 0;
  padding: 0;
  min-height: 22px;
  width: 100%;
  min-width: 0;
  appearance: none;
  -webkit-appearance: none;
}
.ov-field select {
  /* Native arrow removed above, so supply one. Two crossed gradients make a
     hard-edged triangle that aliases badly; a stroked chevron matches the rest
     of the icon set and stays crisp at any zoom. */
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c7f99' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9.5 12 15.5 18 9.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right center;
  background-size: 15px 15px;
  padding-right: 22px;
  cursor: pointer;
}
.ov-field:focus-within select {
  background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b3fd6' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9.5 12 15.5 18 9.5'/%3E%3C/svg%3E");
}
.ov-field input:focus,
.ov-field select:focus { outline: none; }

/* --------------------------------------------------------------------------
   8. Header and navigation
   -------------------------------------------------------------------------- */
.ov-header {
  position: sticky;
  top: 0;
  z-index: 60;
  /* A strip of photograph above the bar, so it reads as floating rather than
     welded to the edge. The hero's -110px pull is larger than the header at any
     of these values, so no gap can open behind it. */
  padding: clamp(14px, 1.8vh, 22px) var(--ov-gutter) 0;
  transition: filter .3s var(--ov-ease);
}
.ov-header.is-scrolled { filter: drop-shadow(0 12px 26px rgba(17, 68, 112, .18)); }

.ov-header__shell {
  max-width: var(--ov-max-wide);
  margin-inline: auto;
  position: relative;
  border-radius: var(--ov-r-lg);
  overflow: hidden;
  box-shadow: 0 14px 34px rgba(17, 68, 112, .14);
}
.ov-header__shell::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(242, 249, 255, .35);
}

.ov-header__bar {
  position: relative;
  background: linear-gradient(140deg, rgba(255,255,255,.66), rgba(255,255,255,.42));
  -webkit-backdrop-filter: blur(30px) saturate(1.15);
  backdrop-filter: blur(30px) saturate(1.15);
  border: 1px solid var(--ov-glass-brd);
  border-radius: var(--ov-r-lg);
  padding: 12px 18px;
  display: flex;
  align-items: center;
  gap: 18px;
}

.ov-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(20px, 1.5vw, 24px);
  letter-spacing: -.025em;
  color: var(--ov-ink);
  white-space: nowrap;
}
/* The lockup is now two images from the logo file, not an image plus type set
   in the theme's display face. A logo should not be approximated. */
.ov-brand__mark {
  width: clamp(54px, 4.4vw, 72px);
  height: auto;
  filter: drop-shadow(0 2px 6px rgba(17, 68, 112, .2));
}
/* Custom logo set in the Customizer keeps a single sensible size. */
.ov-brand > img:only-child { width: clamp(120px, 10vw, 170px); height: auto; }
.ov-brand:hover { color: var(--ov-ink); }

.ov-nav {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  font-size: 14px;
  margin-left: auto;
}
.ov-nav > li { list-style: none; position: relative; }
.ov-nav ul { list-style: none; margin: 0; padding: 0; }
.ov-nav a { color: var(--ov-body); }
.ov-nav > li > a { color: var(--ov-ink); font-weight: 500; }

/* Submenu: opens on hover and on keyboard focus */
.ov-nav .sub-menu {
  position: absolute;
  top: calc(100% + 14px);
  left: -14px;
  min-width: 216px;
  display: grid;
  gap: 10px;
  padding: 16px;
  border-radius: var(--ov-r-md);
  background: rgba(255, 255, 255, .9);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  border: 1px solid var(--ov-glass-brd);
  box-shadow: 0 22px 44px rgba(17, 68, 112, .18);
  z-index: 70;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity .2s var(--ov-ease), transform .2s var(--ov-ease), visibility .2s;
}
.ov-nav > li:hover > .sub-menu,
.ov-nav > li:focus-within > .sub-menu {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* Dropdown arrow */
.ov-nav .menu-item-has-children > a::after {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  border-right: 2px solid var(--ov-teal);
  border-bottom: 2px solid var(--ov-teal);
  transform: rotate(45deg);
  margin-left: 7px;
  margin-bottom: 2px;
}

/* Burger button (mobile only) */
.ov-burger {
  display: none;
  margin-left: auto;
  width: 44px; height: 44px;
  border-radius: 12px;
  border: 1px solid var(--ov-glass-brd-2);
  background: rgba(255, 255, 255, .7);
  cursor: pointer;
  padding: 0;
  place-items: center;
}
.ov-burger span {
  display: block;
  width: 18px; height: 2px;
  background: var(--ov-ink);
  border-radius: 2px;
  transition: transform .25s var(--ov-ease), opacity .2s;
}
.ov-burger span + span { margin-top: 4px; }
.ov-burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.ov-burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.ov-burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* --------------------------------------------------------------------------
   10. Floating search bar + statistics
   -------------------------------------------------------------------------- */
.ov-searchband {
  position: relative;
  z-index: 3;
  max-width: var(--ov-max);
  /* Climbs into the hero instead of sitting under it. The wave already fades
     the photo here, so the card lands on soft ground. */
  margin: clamp(-185px, -13.2vh, -115px) auto 0;
  padding-inline: var(--ov-gutter);
}

.ov-searchbox {
  position: relative;
  border-radius: var(--ov-r-xl);
  overflow: hidden;
  box-shadow: 0 26px 60px rgba(17, 68, 112, .22);
}
.ov-searchbox__tint { position: absolute; inset: 0; background: #f8fcff; }
.ov-searchbox__body {
  position: relative;
  background: var(--ov-glass-strong);
  -webkit-backdrop-filter: blur(26px);
  backdrop-filter: blur(26px);
  border: 1px solid var(--ov-glass-brd-2);
  border-radius: var(--ov-r-xl);
  padding: clamp(20px, 2.2vw, 30px);
}

.ov-tabs {
  display: flex;
  flex-wrap: wrap;
  margin: 0 0 18px;
  padding: 0 0 2px;
  list-style: none;
  border-bottom: 1px solid rgba(17, 68, 112, .12);
}
.ov-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ov-body-soft);
  font-size: 14px;
  padding: 10px 4px 12px;
  margin-right: 22px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  cursor: pointer;
  transition: color .2s var(--ov-ease), border-color .2s var(--ov-ease);
}
.ov-tab .ov-icon { width: 18px; height: 18px; opacity: .85; }
.ov-tab:hover { color: var(--ov-ink); }
.ov-tab.is-active {
  color: var(--ov-ink);
  font-weight: 600;
  border-bottom-color: var(--ov-violet);
}
.ov-tab.is-active .ov-icon { color: var(--ov-violet); opacity: 1; }

.ov-searchgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 12px;
  align-items: end;
}

.ov-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  margin-top: clamp(20px, 3vh, 34px);
  padding: 0;
  list-style: none;
}
.ov-stat {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 6px 22px;
  /* Hairline rules instead of four separate cards: one continuous band reads
     as a single statement about the business rather than four unrelated ones. */
  border-left: 1px solid rgba(17, 68, 112, .14);
}
.ov-stat:first-child { border-left: 0; padding-left: 4px; }

.ov-stat__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 16px;
  color: var(--ov-teal-deep);
  background: rgba(255, 255, 255, .62);
  border: 1px solid rgba(255, 255, 255, .9);
  box-shadow: 0 8px 18px rgba(17, 68, 112, .1);
}
.ov-stat__badge .ov-icon { width: 22px; height: 22px; }
.ov-stat--accent .ov-stat__badge { color: var(--ov-violet); background: rgba(243, 239, 255, .8); }

.ov-stat__text { display: grid; min-width: 0; }
.ov-stat__num {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(23px, 2.2vw, 30px);
  line-height: 1.1;
  color: var(--ov-ink);
}
.ov-stat--accent .ov-stat__num { color: var(--ov-violet-mid); }
.ov-stat__label { font-size: 12.5px; color: var(--ov-muted); }


.ov-grid { display: grid; gap: 18px; }
.ov-grid--3 { grid-template-columns: repeat(auto-fit, minmax(268px, 1fr)); }
.ov-grid--cards { grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); }
.ov-grid--steps { grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); }

.ov-stack { display: grid; gap: 12px; }

/* --------------------------------------------------------------------------
   13. Membership
   -------------------------------------------------------------------------- */
.ov-mem {
  display: grid;
  /* Slightly wider on the right: two enumerated steps need more room than a
     photograph, and equal halves left the copy cramped. */
  grid-template-columns: minmax(0, .88fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}

.ov-mem__media {
  position: relative;
  border-radius: clamp(20px, 2.4vw, 30px);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  box-shadow: 0 34px 70px rgba(17, 68, 112, .22);
}
.ov-mem__media img { width: 100%; height: 100%; object-fit: cover; }

/* 13.1 The rate card overlaid on the photograph */
.ov-rate {
  position: absolute;
  left: clamp(14px, 2vw, 26px);
  bottom: clamp(14px, 2vw, 26px);
  right: clamp(14px, 2vw, 26px);
  max-width: 300px;
  border-radius: 20px;
  overflow: hidden;
}
.ov-rate__body {
  background: rgba(255, 255, 255, .82);
  -webkit-backdrop-filter: blur(26px) saturate(1.4);
  backdrop-filter: blur(26px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, .9);
  border-radius: 20px;
  padding: 18px 20px 20px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9);
}
.ov-rate__label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ov-violet-mid);
  margin-bottom: 10px;
}
.ov-rate__label .ov-icon { width: 16px; height: 16px; }
.ov-rate__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
}
.ov-rate__price strong {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(30px, 3vw, 38px);
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--ov-violet-mid);
}
.ov-rate__price span { font-size: 15px; color: var(--ov-ink); }

/* The rung the rest of the page already promises. Separated by a rule so it
   reads as the end of a ladder, not as a fourth competing number. */
.ov-rate__points {
  margin: 2px 0 10px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--ov-body);
}
.ov-rate__property { font-size: 12.5px; color: var(--ov-body); }

/* 13.2 The two enumerated steps */
.ov-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 34px; }

.ov-step2 {
  position: relative;
  display: grid;
  grid-template-columns: 52px 56px 1fr;
  align-items: start;
  gap: 16px;
}
/* Rule joining one step to the next, drawn from the number itself. */
.ov-step2:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 26px;
  top: 58px;
  bottom: -40px;
  width: 1px;
  background: linear-gradient(180deg, rgba(17, 68, 112, .2), rgba(17, 68, 112, 0));
}

.ov-step2__num {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: #fff;
  border: 1px solid rgba(17, 68, 112, .12);
  box-shadow: 0 10px 22px rgba(17, 68, 112, .1);
  font-family: var(--ov-font-display);
  font-weight: 600;
  font-size: 16px;
  color: var(--ov-ink);
}
.ov-step2__badge {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 18px;
  background: rgba(243, 239, 255, .9);
  color: var(--ov-violet);
}
.ov-step2__badge .ov-icon { width: 26px; height: 26px; }

.ov-step2__body h3 { margin-bottom: 4px; font-size: 19px; }
.ov-step2__lede {
  font-size: 15px;
  font-weight: 600;
  color: var(--ov-violet-mid);
  margin-bottom: 8px;
}
.ov-step2__body .ov-text { max-width: 46ch; }

.ov-chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
}
/* Flat, not raised. A white pill with a drop shadow is button language, and
   these do not do anything when clicked. */
.ov-chiprow li {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  color: var(--ov-body);
  background: rgba(255, 255, 255, .55);
  border: 1px solid rgba(17, 68, 112, .1);
  border-radius: 999px;
  padding: 7px 13px;
}
.ov-chiprow .ov-icon { width: 17px; height: 17px; color: var(--ov-body-soft); }

.ov-pointchip {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
  font-size: 13.5px;
  color: var(--ov-ink);
  background: rgba(243, 239, 255, .85);
  border-radius: 999px;
  padding: 9px 16px;
}
.ov-pointchip strong { color: var(--ov-violet-mid); font-weight: 600; }
.ov-pointchip .ov-icon { width: 16px; height: 16px; color: var(--ov-violet); }

/* 13.3 Text link with a rule that extends on hover */
.ov-textlink {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-top: 30px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ov-ink);
  padding-bottom: 5px;
  border-bottom: 1.5px solid var(--ov-violet);
}
.ov-textlink .ov-icon { width: 18px; height: 18px; color: var(--ov-violet); transition: transform .3s var(--ov-ease); }
.ov-textlink:hover { color: var(--ov-violet-mid); }
.ov-textlink:hover .ov-icon { transform: translateX(4px); }

@media (max-width: 900px) {
  .ov-mem { grid-template-columns: 1fr; }
  .ov-step2 { grid-template-columns: 46px 50px 1fr; gap: 12px; }
  .ov-step2:not(:last-child)::before { left: 23px; }
}

/* --------------------------------------------------------------------------
   14. Member stories
   -------------------------------------------------------------------------- */
.ov-stories { position: relative; overflow: hidden; }

/* The brand mark as texture, the same device as the Travel points slab. */
.ov-stories__mark {
  position: absolute;
  /* Fully inside the section. Bleeding it past the edge worked on the dark
     slab, where a visible rounded border made the crop read as intentional;
     here the boundary is invisible, so a clipped mark just looks broken. */
  top: clamp(12px, 2.4vw, 34px);
  right: clamp(12px, 2.4vw, 34px);
  width: min(42%, 460px);
  aspect-ratio: 914 / 386;
  background: url("../img/ocean-view-mark.png") no-repeat center / contain;
  opacity: .085;
  pointer-events: none;
  z-index: 0;
}

.ov-stories > .ov-wrap { position: relative; z-index: 1; }
.ov-stories__head { max-width: 46ch; margin-bottom: clamp(28px, 4vw, 44px); }

.ov-stories__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: clamp(18px, 2.4vw, 28px);
  align-items: stretch;
}

/* 14.1 The featured story: photograph with the quote laid over its foot */
.ov-story-hero {
  position: relative;
  margin: 0;
  border-radius: clamp(18px, 2vw, 26px);
  overflow: hidden;
  min-height: 460px;
  display: flex;
  box-shadow: 0 30px 62px rgba(17, 68, 112, .22);
}
.ov-story-hero__media { position: absolute; inset: 0; }
.ov-story-hero__media img { width: 100%; height: 100%; object-fit: cover; }

.ov-story-hero__body {
  position: relative;
  margin-top: auto;
  width: 100%;
  padding: clamp(22px, 3vw, 34px);
  color: #fff;
  /* Dark wash only at the foot, so the photograph keeps its top two thirds. */
  background: linear-gradient(180deg,
    rgba(5, 22, 42, 0) 0%,
    rgba(5, 22, 42, .55) 32%,
    rgba(5, 22, 42, .88) 100%);
}
.ov-story-hero__mark {
  display: block;
  font-family: var(--ov-font-display);
  font-size: 58px;
  line-height: .5;
  color: rgba(179, 164, 255, .85);
  margin-bottom: 10px;
}
.ov-story-hero blockquote {
  margin: 0 0 22px;
  font-family: var(--ov-font-display);
  font-weight: 600;
  font-size: clamp(21px, 2.3vw, 30px);
  line-height: 1.24;
  letter-spacing: -.02em;
  max-width: 20ch;
}
.ov-story-hero__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.ov-story-hero .ov-story__name { color: #fff; }
.ov-story-hero .ov-story__meta { color: rgba(226, 241, 255, .72); }

.ov-story__chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  color: var(--ov-ink);
  background: rgba(255, 255, 255, .9);
  border-radius: 999px;
  padding: 9px 16px;
}
.ov-story__chip .ov-icon { width: 16px; height: 16px; color: var(--ov-violet); }
.ov-story__chip--inline {
  margin-top: 14px;
  justify-self: start;
  font-size: 13px;
  color: var(--ov-ink);
  background: rgba(243, 239, 255, .9);
  padding: 7px 13px;
}
.ov-stories__cta { text-align: center; margin-top: clamp(26px, 3.5vw, 40px); }
.ov-stories__cta .ov-textlink { margin-top: 0; }

/* 14.2 The two supporting stories */
.ov-stories__side { display: grid; gap: clamp(18px, 2.4vw, 28px); }

.ov-story-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 38%;
  gap: 0;
  margin: 0;
  background: #fff;
  border-radius: clamp(16px, 1.8vw, 22px);
  overflow: hidden;
  box-shadow: 0 18px 40px rgba(17, 68, 112, .12);
  transition: transform .45s var(--ov-ease), box-shadow .45s var(--ov-ease);
}
.ov-story-card__body { padding: clamp(18px, 2.2vw, 26px); display: grid; align-content: center; }
/* Three tiers, not one. The quote was 15px regular against a 14.5px semibold
   name: two pixels between a member's voice and their signature, so the eye had
   nowhere to land. */
.ov-story-card blockquote {
  margin: 10px 0 16px;
  font-family: var(--ov-font-display);
  font-weight: 600;
  font-size: clamp(17px, 1.6vw, 20px);
  line-height: 1.34;
  letter-spacing: -.015em;
  color: var(--ov-ink);
}
.ov-story-card__media img { width: 100%; height: 100%; object-fit: cover; }


.ov-story__name { font-size: 14.5px; font-weight: 600; color: var(--ov-ink); }
.ov-story__meta { font-size: 12px; color: var(--ov-muted); }

/* The place name is the most evocative fact in a travel testimonial, and it was
   buried mid-string in 12px grey. */
.ov-story__place {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ov-violet-mid);
}
.ov-story__place .ov-icon { width: 14px; height: 14px; }
.ov-story__place--over {
  position: absolute;
  top: clamp(14px, 2vw, 22px);
  left: clamp(14px, 2vw, 22px);
  z-index: 2;
  color: var(--ov-ink);
  background: rgba(255, 255, 255, .88);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-radius: 999px;
  padding: 7px 14px 7px 11px;
}

/* 14.3 Closing figures */
/* A container of its own, so the centred alignment reads as a deliberate change
   of register rather than as the section losing its axis halfway down. */
.ov-stories__strip { margin-top: clamp(30px, 4.5vw, 52px); }
.ov-stories__stripinner {
  background: rgba(255, 255, 255, .55);
  border: 1px solid rgba(255, 255, 255, .8);
  border-radius: clamp(18px, 2vw, 26px);
  padding: clamp(24px, 3.2vw, 38px) clamp(18px, 3vw, 34px);
  box-shadow: 0 14px 34px rgba(17, 68, 112, .08);
}
.ov-stories__striptitle {
  text-align: center;
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ov-violet-mid);
  margin-bottom: 22px;
}
/* Same band as under the search bar, without its rules: this one closes a
   section rather than dividing one. */
.ov-stats--plain { margin-top: 0; justify-items: center; }
.ov-stats--plain .ov-stat { border-left: 0; justify-content: center; }

@media (max-width: 900px) {
  .ov-stories__grid { grid-template-columns: 1fr; }
  .ov-story-hero { min-height: 400px; }
  .ov-stories__mark { width: 58%; opacity: .07; }
}
@media (max-width: 520px) {
  .ov-story-card { grid-template-columns: 1fr; }
  .ov-story-card__media { height: 200px; }
}

/* --------------------------------------------------------------------------
   15. Savings — bento grid
   Twelve columns so cards can take unequal widths. Three carry photographs and
   three do not, which is what stops six equal tiles reading as a spreadsheet.
   -------------------------------------------------------------------------- */
.ov-bento {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(12px, 1.5vw, 20px);
}
.ov-bento__head       { grid-column: span 4; align-self: center; padding-right: 10px; }
.ov-bcard--hotels     { grid-column: span 8; min-height: 300px; }
.ov-bcard--cruises    { grid-column: span 5; min-height: 186px; }
.ov-bcard--flights    { grid-column: span 3; }
.ov-bcard--cars       { grid-column: span 4; }
.ov-bcard--activities { grid-column: span 4; }
.ov-bcard--resort     { grid-column: span 8; min-height: 240px; }

.ov-bento__head .ov-h2 em { font-style: normal; color: var(--ov-violet); display: block; }

.ov-bcard {
  position: relative;
  border-radius: clamp(16px, 1.8vw, 24px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* 15.1 Cards carrying a photograph */
.ov-bcard--photo { box-shadow: 0 22px 48px rgba(17, 68, 112, .2); }
.ov-bcard__media { position: absolute; inset: 0; }
.ov-bcard__media img { width: 100%; height: 100%; object-fit: cover; }
.ov-bcard--photo .ov-bcard__body {
  position: relative;
  height: 100%;
  padding: clamp(18px, 2.2vw, 28px);
  color: #fff;
  /* The wash runs bottom-up and the type sits at the foot of it, so the
     photograph keeps its subject and the words keep the darkest part.

     This was a sideways wash once, dark down the left and clear by 82%, which
     suits a wide card with its reading column on one side. These are not that:
     six cards across a three-column bento, each about as narrow as the phone
     layout, with the type running the full width — the second line walked
     straight out of the gradient. The phone breakpoint had already worked this
     out for itself; this is the same treatment, now that the desktop cards
     have turned out to need it too. */
  background: linear-gradient(to top,
    rgba(6, 26, 48, .94) 0%,
    rgba(6, 26, 48, .82) 34%,
    rgba(6, 26, 48, .46) 62%,
    rgba(6, 26, 48, .12) 100%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.ov-bcard--photo .ov-bcard__label { color: rgba(226, 241, 255, .85); }
.ov-bcard--photo .ov-bcard__note  { color: rgba(226, 241, 255, .8); }
.ov-bcard--photo .ov-bcard__badge { background: rgba(255, 255, 255, .16); color: #fff; }

/* 15.2 Cards without one */
.ov-bcard--plain {
  padding: clamp(18px, 2.2vw, 26px);
  background: rgba(255, 255, 255, .58);
  border: 1px solid rgba(255, 255, 255, .85);
  box-shadow: 0 14px 32px rgba(17, 68, 112, .1);
}

/* 15.3 Shared parts */
.ov-bcard__badge {
  display: grid;
  place-items: center;
  width: clamp(38px, 3.4vw, 46px);
  height: clamp(38px, 3.4vw, 46px);
  border-radius: 14px;
  margin-bottom: 14px;
  background: rgba(243, 239, 255, .9);
  color: var(--ov-violet);
}
.ov-bcard__badge .ov-icon { width: 21px; height: 21px; }

.ov-bcard__label {
  font-size: 11.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ov-muted);
  margin-bottom: 8px;
}
.ov-bcard__figure {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(28px, 3vw, 40px);
  line-height: 1;
  letter-spacing: -.03em;
}
.ov-bcard__figure--sm { font-size: clamp(21px, 2.1vw, 28px); line-height: 1.12; max-width: 12ch; }
.ov-bcard__note { font-size: 14px; line-height: 1.5; margin-top: 6px; }
.ov-bcard__lede { font-size: 15px; line-height: 1.45; color: var(--ov-ink); margin-bottom: 14px; max-width: 24ch; }
.ov-bcard__big {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(30px, 3.2vw, 42px);
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--ov-violet-mid);
  margin-top: auto;
}
.ov-bcard__big--words { font-size: clamp(22px, 2.2vw, 30px); line-height: 1.08; }
.ov-bcard__unit { font-size: 13px; color: var(--ov-muted); margin-top: 6px; }

/* The figure that matters, lifted out of the wash onto its own glass. */
.ov-bcard__chip {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: auto;
  align-self: flex-start;
  padding: 10px 16px;
  border-radius: 16px;
  background: rgba(255, 255, 255, .9);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  color: var(--ov-body);
  font-size: 12.5px;
  line-height: 1.3;
}
.ov-bcard__chip strong {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(22px, 2.2vw, 30px);
  line-height: 1;
  color: var(--ov-violet-mid);
}

/* The middle row runs shorter than the other two. Its height was set by the
   plain cards' own content, not by the cruise card, so trimming that card
   alone would have changed nothing. */
.ov-bcard--flights,
.ov-bcard--cars {
  padding-block: clamp(14px, 1.6vw, 19px);
}
.ov-bcard--flights .ov-bcard__badge,
.ov-bcard--cars .ov-bcard__badge {
  width: 36px;
  height: 36px;
  border-radius: 12px;
  margin-bottom: 10px;
}
.ov-bcard--flights .ov-bcard__badge .ov-icon,
.ov-bcard--cars .ov-bcard__badge .ov-icon { width: 18px; height: 18px; }
.ov-bcard--flights .ov-bcard__lede,
.ov-bcard--cars .ov-bcard__lede { margin-bottom: 10px; font-size: 14px; }
.ov-bcard--flights .ov-bcard__big,
.ov-bcard--cars .ov-bcard__big { font-size: clamp(24px, 2.5vw, 33px); }
.ov-bcard--cars .ov-bcard__big--words { font-size: clamp(19px, 1.9vw, 25px); }
.ov-bcard--flights .ov-bcard__unit,
.ov-bcard--cars .ov-bcard__unit { margin-top: 4px; }

/* 15.4 Closing bar */
.ov-trustbar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 4px;
  margin: clamp(16px, 2vw, 24px) 0 0;
  padding: clamp(14px, 1.8vw, 20px) clamp(10px, 2vw, 24px);
  list-style: none;
  background: rgba(255, 255, 255, .62);
  border: 1px solid rgba(255, 255, 255, .85);
  border-radius: 999px;
  box-shadow: 0 14px 32px rgba(17, 68, 112, .08);
}
.ov-trustbar li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 18px;
  border-left: 1px solid rgba(17, 68, 112, .12);
}
.ov-trustbar li:first-child { border-left: 0; }
.ov-trustbar__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(243, 239, 255, .9);
  color: var(--ov-violet);
}
.ov-trustbar__badge .ov-icon { width: 20px; height: 20px; }
.ov-trustbar__text { display: grid; }
.ov-trustbar__top { font-size: 13.5px; color: var(--ov-body); }
.ov-trustbar__sub { font-size: 13.5px; font-weight: 600; color: var(--ov-ink); }

@media (max-width: 900px) {
  .ov-bento__head,
  .ov-bcard--hotels,
  .ov-bcard--cruises,
  .ov-bcard--resort { grid-column: span 12; }
  .ov-bcard--flights,
  .ov-bcard--cars,
  .ov-bcard--activities { grid-column: span 6; }
  .ov-trustbar { border-radius: clamp(18px, 3vw, 26px); }
  .ov-trustbar li { border-left: 0; }
}
@media (max-width: 560px) {
  .ov-bcard--flights,
  .ov-bcard--cars,
  .ov-bcard--activities { grid-column: span 12; }
}

/* --------------------------------------------------------------------------
   16. World tours
   One featured trip beside a two-by-two grid. Unequal weight again: the
   featured card is the one the business is pushing, and five equal tiles said
   nothing about which.
   -------------------------------------------------------------------------- */
.ov-tours__head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: clamp(22px, 3vw, 34px);
}
.ov-tours__head .ov-h2 em { font-style: normal; color: var(--ov-violet); display: block; }

.ov-pillbtn {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  flex: none;
  padding: 8px 8px 8px 22px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .9);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  border: 1px solid rgba(255, 255, 255, .95);
  box-shadow: 0 12px 28px rgba(17, 68, 112, .14);
  font-size: 15px;
  font-weight: 600;
  color: var(--ov-ink);
  transition: transform .3s var(--ov-ease), box-shadow .3s var(--ov-ease);
}
.ov-pillbtn__arrow {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--ov-violet);
  color: #fff;
  transition: transform .3s var(--ov-ease);
}
.ov-pillbtn__arrow .ov-icon { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) {
  .ov-pillbtn:hover { transform: translateY(-2px); box-shadow: 0 18px 36px rgba(17, 68, 112, .2); color: var(--ov-ink); }
  .ov-pillbtn:hover .ov-pillbtn__arrow { transform: translateX(3px); }
}

.ov-tours__grid {
  display: grid;
  grid-template-columns: 1.32fr 1fr 1fr;
  grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: clamp(12px, 1.5vw, 20px);
}
.ov-tcard--featured { grid-row: span 2; }

.ov-tcard {
  position: relative;
  min-height: 236px;
  border-radius: clamp(16px, 1.8vw, 22px);
  overflow: hidden;
  display: flex;
  box-shadow: 0 20px 44px rgba(17, 68, 112, .18);
  transition: transform .45s var(--ov-ease), box-shadow .45s var(--ov-ease);
}
.ov-tcard--featured { min-height: 480px; }
.ov-tcard__media { position: absolute; inset: 0; }
.ov-tcard__media img,
.ov-tcard__media .ov-imgslot { width: 100%; height: 100%; object-fit: cover; }

.ov-tcard__badge {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 14px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .92);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ov-ink);
}
.ov-tcard__badge .ov-icon { width: 14px; height: 14px; color: var(--ov-violet); }

.ov-tcard__body {
  position: relative;
  margin-top: auto;
  width: 100%;
  padding: clamp(16px, 1.8vw, 24px);
  color: #fff;
  /* Deep enough at the foot to hold small type, gone by halfway up so the
     photograph keeps its subject. */
  background: linear-gradient(180deg,
    rgba(4, 18, 36, 0) 0%,
    rgba(4, 18, 36, .55) 34%,
    rgba(4, 18, 36, .9) 100%);
}
.ov-tcard__city {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(17px, 1.7vw, 22px);
  line-height: 1.1;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: #fff;
}
.ov-tcard--featured .ov-tcard__city { font-size: clamp(24px, 2.6vw, 34px); }
.ov-tcard__country { font-size: 13.5px; color: rgba(226, 241, 255, .82); margin-top: 2px; }

.ov-tcard__meta {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  font-size: 12.5px;
  color: rgba(226, 241, 255, .82);
}
.ov-tcard__meta .ov-icon { width: 15px; height: 15px; }

.ov-tcard__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, .18);
}
.ov-tcard__price { font-size: 13px; color: rgba(226, 241, 255, .82); }
.ov-tcard__price strong {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(19px, 1.9vw, 25px);
  color: #b3a4ff;
  margin-left: 4px;
}
.ov-tcard__go {
  display: grid;
  place-items: center;
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  /* Solid fallback. The glass version lives in the @supports block below, so
     a browser without backdrop-filter still gets a legible white disc. */
  background: #fff;
  color: var(--ov-ink);
  transition: transform .3s var(--ov-ease), background-color .2s, border-color .2s;
}
.ov-tcard__go .ov-icon { width: 18px; height: 18px; }

/* The whole card is the link. Above the photograph, the panel and the hover
   glare, and inheriting the card's corners so the focus ring traces the card
   instead of cutting across it. */
.ov-tcard__link {
  position: absolute;
  inset: 0;
  z-index: 4;
  border-radius: inherit;
}

@media (hover: hover) and (pointer: fine) {
  /* Pointing anywhere on the card lights its arrow, since anywhere on the card
     is now what you are about to click. */
  .ov-tcard:hover .ov-tcard__go {
    background: var(--ov-violet);
    border-color: var(--ov-violet);
    color: #fff;
    transform: translateX(3px);
  }
}

@media (max-width: 900px) {
  .ov-tours__grid { grid-template-columns: 1fr 1fr; grid-template-rows: none; }
  .ov-tcard--featured { grid-column: span 2; grid-row: auto; min-height: 340px; }
}
@media (max-width: 560px) {
  .ov-tours__grid { grid-template-columns: 1fr; }
  .ov-tcard--featured { grid-column: span 1; }
}

/* --------------------------------------------------------------------------
   17. How it works
   Four cards on a dashed path, staggered so the row reads as a journey rather
   than as a table. Each gap carries its own curved connector, sized off the
   stagger so the two always agree.
   -------------------------------------------------------------------------- */
.ov-how__head { max-width: 34ch; margin-bottom: clamp(28px, 4vw, 46px); }

/* A wrapper, not the list itself: an <ol> may only contain <li>, and the path
   decorations are not list items.

   Geometry note. The stagger and the connectors are tied together by two custom
   properties, because the numbers are not independent: the curve has to travel
   exactly as far vertically as the stagger drops. The SVG draws from y=8 to
   y=52 inside a 60-unit box, so it covers 44/60 of its own height, which makes
   the slot 60/44 = 1.364 times the stagger, and the slot has to start 8/60 of
   that above the anchor — 0.182 of a stagger. Change --ov-stagger alone and the
   whole thing still lines up. */
.ov-steps3 {
  position: relative;
  --ov-stagger: clamp(28px, 3.8vw, 56px);
  --ov-conn-h: calc(var(--ov-stagger) * 1.364);
  --ov-conn-anchor: clamp(96px, 11vw, 132px);
  --ov-gap: clamp(22px, 3.4vw, 48px);
  --ov-col: calc((100% - 3 * var(--ov-gap)) / 4);
}
.ov-steps3__list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* Wider than the other grids on purpose: the connectors live in these gaps,
     and a curve needs room to read as a curve rather than as a kink. */
  gap: var(--ov-gap);
  align-items: start;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* One connector per gap. Not one line behind the row: the cards are
   translucent, so a line spanning the whole width showed straight through their
   interiors — which is what made the row look broken rather than joined. */
.ov-conn-slot {
  position: absolute;
  z-index: 0;
  width: 56px;
  margin-left: -28px;
  height: var(--ov-conn-h);
  top: calc(var(--ov-conn-anchor) - var(--ov-stagger) * .182);
  color: rgba(14, 143, 134, .55);
}
/* The gap centres, derived from the grid rather than guessed at 25/50/75%.
   Those percentages are only the gap centres when the gaps are hairlines; at
   48px they sit 12px off, which on a 48px gap is a quarter of the way into a
   card. Centre n = n columns + (n - 0.5) gaps. */
.ov-conn-slot--1 { left: calc(1 * var(--ov-col) + 0.5 * var(--ov-gap)); }
.ov-conn-slot--2 { left: calc(2 * var(--ov-col) + 1.5 * var(--ov-gap)); }
.ov-conn-slot--3 { left: calc(3 * var(--ov-col) + 2.5 * var(--ov-gap)); }
.ov-conn,
.ov-conn svg { display: block; width: 100%; height: 100%; }
.ov-conn { position: relative; }

/* The ring where the path leaves a card, sitting on the path's own start. */
.ov-conn__dot {
  position: absolute;
  left: 0;
  width: 11px;
  height: 11px;
  margin: -5.5px 0 0 -5.5px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid rgba(14, 143, 134, .7);
  box-shadow: 0 2px 8px rgba(17, 68, 112, .12);
}

.ov-step3 {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  /* No min-height. The reference cards are tall because a photograph fills
     their lower half; reserving that height with no photo to put in it just
     prints an empty box. A card with a photo grows on its own. */
  padding: clamp(18px, 2vw, 26px) clamp(18px, 2vw, 26px) 0;
  border-radius: clamp(16px, 1.8vw, 22px);
  overflow: hidden;
  /* Opaque enough that a connector clipping the card edge disappears under it
     instead of ghosting through. .58 was not. */
  background: rgba(255, 255, 255, .74);
  border: 1px solid rgba(255, 255, 255, .85);
  box-shadow: 0 18px 42px rgba(17, 68, 112, .12);
}
/* The drop the connectors are drawn to meet. */
.ov-step3:nth-child(even) { margin-top: var(--ov-stagger); }

.ov-step3__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.ov-step3__num {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(34px, 3.6vw, 48px);
  line-height: 1;
  letter-spacing: -.03em;
  color: rgba(139, 118, 232, .9);
}
.ov-step3__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .92);
  color: var(--ov-violet);
  box-shadow: 0 8px 20px rgba(17, 68, 112, .12);
}
.ov-step3__badge .ov-icon { width: 21px; height: 21px; }

.ov-step3__title {
  font-family: var(--ov-font-display);
  font-weight: 600;
  font-size: clamp(17px, 1.7vw, 21px);
  line-height: 1.2;
  color: var(--ov-ink);
  margin-bottom: 8px;
}
.ov-step3__text {
  font-size: 14px;
  line-height: 1.6;
  color: var(--ov-body);
  margin-bottom: clamp(16px, 2vw, 22px);
}

/* Bleeds to the card's own edges: the padding stops above it. */
.ov-step3__media {
  margin: auto calc(clamp(18px, 2vw, 26px) * -1) 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.ov-step3__media img { width: 100%; height: 100%; object-fit: cover; }

.ov-how__cta { text-align: center; margin-top: clamp(30px, 4vw, 48px); }

/* --------------------------------------------------------------------------
   Travel points
   One panel: the explanation, one photograph, and the price crossing the foot
   of both. The bar is a grid row, not an absolutely positioned strip, and the
   photograph spans both rows behind it. So the bar can never land on top of
   text it did not reserve room for, however long the copy grows.
   -------------------------------------------------------------------------- */
.ov-ppanel {
  --ov-slab-bg: #f7f4ec;                 /* the fish punch their eye out of this */
  position: relative;
  display: grid;
  grid-template-columns: 1.02fr 1fr;
  grid-template-rows: auto auto;
  border-radius: clamp(20px, 2.4vw, 30px);
  overflow: hidden;
  background: var(--ov-slab-bg);
  box-shadow: 0 30px 70px rgba(17, 68, 112, .16);
}

.ov-ppanel__text {
  position: relative;
  grid-area: 1 / 1 / 2 / 2;
  padding: clamp(28px, 3.4vw, 54px) clamp(22px, 2.6vw, 44px) clamp(20px, 2.4vw, 34px);
}
.ov-ppanel__inner { position: relative; z-index: 2; }

.ov-ppanel__media {
  position: relative;
  grid-area: 1 / 2 / 3 / 3;              /* runs under the bar, so it shows below it */
  min-height: clamp(320px, 40vw, 560px);
  overflow: hidden;
}
.ov-ppanel__media img,
.ov-ppanel__media .ov-imgslot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ov-pfeat { list-style: none; margin: clamp(20px, 2.4vw, 30px) 0 0; padding: 0; }
.ov-pfeat li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding-block: clamp(9px, 1.1vw, 13px);
}
.ov-pfeat__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: #e3efec;
  color: #0e8f86;
}
.ov-pfeat__badge .ov-icon { width: 20px; height: 20px; }
.ov-pfeat__title {
  display: block;
  font-weight: 700;
  font-size: clamp(15px, 1.5vw, 17px);
  color: var(--ov-ink);
  margin-bottom: 2px;
}
.ov-pfeat__note { display: block; font-size: 13.5px; line-height: 1.5; color: var(--ov-body); }

.ov-pstats {
  display: flex;
  gap: clamp(20px, 3vw, 46px);
  list-style: none;
  margin: clamp(16px, 2vw, 24px) 0 0;
  padding: clamp(16px, 2vw, 24px) 0 0;
  border-top: 1px solid rgba(17, 68, 112, .12);
}
.ov-pstats li { display: flex; align-items: center; gap: 12px; }
.ov-pstats__icon { flex: none; color: #0e8f86; opacity: .75; }
.ov-pstats__icon .ov-icon { width: 34px; height: 34px; }
.ov-pstats__num {
  display: block;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.05;
  color: var(--ov-ink);
}
.ov-pstats__label { display: block; font-size: 13px; color: var(--ov-body); }

/* The saving, floated over the photograph. */
.ov-pcard {
  position: absolute;
  z-index: 2;
  top: clamp(16px, 2.2vw, 34px);
  right: clamp(16px, 2.2vw, 34px);
  max-width: 300px;
  padding: clamp(16px, 1.8vw, 24px) clamp(18px, 2vw, 28px);
  border-radius: clamp(14px, 1.6vw, 20px);
  /* Opaque tint first. The blur is progressive enhancement; without this
     fallback the text sits on bare photograph wherever it is unsupported. */
  background: rgba(255, 255, 255, .93);
  box-shadow: 0 18px 44px rgba(17, 68, 112, .2);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ov-pcard {
    background: rgba(255, 255, 255, .82);
    -webkit-backdrop-filter: blur(14px) saturate(1.2);
    backdrop-filter: blur(14px) saturate(1.2);
  }
}
.ov-pcard__top { display: flex; align-items: center; gap: 12px; }
.ov-pcard__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: #dceeea;
  color: #0e8f86;
}
.ov-pcard__badge .ov-icon { width: 21px; height: 21px; }
.ov-pcard__big {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(26px, 3vw, 38px);
  line-height: 1;
  letter-spacing: -.02em;
  color: #0e8f86;
}
.ov-pcard__off { font-size: 15px; color: var(--ov-body); align-self: flex-end; padding-bottom: 4px; }
.ov-pcard__what {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1.5px solid rgba(199, 160, 74, .5);
  font-weight: 700;
  font-size: clamp(14px, 1.4vw, 16px);
  color: var(--ov-ink);
}
.ov-pcard__when { font-size: 13.5px; color: var(--ov-body); margin-top: 2px; }

/* The price, crossing the foot of the panel. */
.ov-pbar {
  position: relative;
  z-index: 3;
  grid-area: 2 / 1 / 3 / 3;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.4vw, 34px);
  margin: 0 clamp(30px, 5vw, 86px) clamp(12px, 1.6vw, 24px) clamp(14px, 1.8vw, 26px);
  padding: clamp(16px, 2vw, 28px) clamp(18px, 2.4vw, 34px);
  border-radius: clamp(14px, 1.8vw, 22px);
  background: #efe9dd;
}
.ov-pbar__value {
  flex: none;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(46px, 6.4vw, 86px);
  line-height: .9;
  letter-spacing: -.03em;
  color: #0e8f86;
  padding-right: clamp(16px, 2.4vw, 34px);
  border-right: 1px solid rgba(17, 68, 112, .18);
}
.ov-pbar__text { flex: 1 1 auto; min-width: 0; }
.ov-pbar__title {
  font-family: var(--ov-font-display);
  font-weight: 600;
  font-size: clamp(18px, 2.1vw, 27px);
  line-height: 1.15;
  color: var(--ov-ink);
}
.ov-pbar__note { font-size: 13.5px; color: var(--ov-body); margin-top: 6px; }
.ov-pbar__cta { flex: none; }
.ov-pbar__cta .ov-icon { width: 18px; height: 18px; }

.ov-textlink--circle { gap: 14px; border-bottom: 0; }
.ov-textlink--circle > span:last-child {
  padding-bottom: 5px;
  border-bottom: 1.5px solid var(--ov-violet);
}
.ov-textlink__circle {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 1.5px solid rgba(91, 63, 214, .45);
  color: var(--ov-violet);
  transition: transform .3s var(--ov-ease), background-color .25s, color .25s;
}
.ov-textlink__circle .ov-icon { width: 18px; height: 18px; transform: rotate(-45deg); }
@media (hover: hover) and (pointer: fine) {
  .ov-textlink--circle:hover .ov-textlink__circle {
    background: var(--ov-violet);
    border-color: var(--ov-violet);
    color: #fff;
    transform: translateY(-2px);
  }
}

@media (max-width: 900px) {
  .ov-steps3__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ov-conn-slot { display: none; }
  .ov-step3:nth-child(even) { margin-top: 0; }
}
@media (max-width: 520px) {
  .ov-steps3__list { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   18. Points
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   19. "About us" band
   -------------------------------------------------------------------------- */
.ov-band {
  position: relative;
  padding: clamp(40px, 5vw, 76px) var(--ov-gutter);
  overflow: hidden;
}
.ov-band__bg {
  position: absolute;
  inset: -14% 0;
  opacity: .85;
}
.ov-band__bg img { width: 100%; height: 100%; object-fit: cover; }
.ov-band__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(228,243,251,.75), rgba(238,234,251,.85));
}
.ov-band__panel {
  position: relative;
  max-width: var(--ov-max);
  margin-inline: auto;
  border-radius: var(--ov-r-xl);
  overflow: hidden;
  box-shadow: 0 26px 56px rgba(17, 68, 112, .16);
}
.ov-band__tint { position: absolute; inset: 0; background: #f8fbff; }
.ov-band__body {
  position: relative;
  background: rgba(255, 255, 255, .6);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
  border: 1px solid var(--ov-glass-brd-2);
  border-radius: var(--ov-r-xl);
  padding: clamp(24px, 4vw, 42px);
}
.ov-band__figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 20px;
}
.ov-figure__num {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 31px;
  color: var(--ov-ink);
}
.ov-figure--violet .ov-figure__num { color: var(--ov-violet-mid); }
.ov-figure__label { margin-top: 3px; font-size: 12.5px; color: var(--ov-muted); }

/* --------------------------------------------------------------------------
   20. Getaways
   Owns its panel classes rather than borrowing the tours carousel's. It did
   borrow them, and rebuilding tours deleted the definitions out from under it.
   -------------------------------------------------------------------------- */
.ov-gpanel {
  position: relative;
  margin: auto 12px 12px;
  border-radius: var(--ov-r-md);
  overflow: hidden;
}
.ov-gpanel__tint { position: absolute; inset: 0; background: var(--ov-tint-blue); }
.ov-gpanel__body {
  position: relative;
  background: rgba(255, 255, 255, .66);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  border: 1px solid var(--ov-glass-brd-2);
  border-radius: var(--ov-r-md);
  padding: 18px;
}

/* --------------------------------------------------------------------------
   20b. Getaways
   -------------------------------------------------------------------------- */
.ov-getaway {
  position: relative;
  border-radius: 24px;
  overflow: hidden;
  min-height: 400px;
  display: flex;
  flex-direction: column;
  box-shadow: var(--ov-sh-xl);
  transition: transform .45s var(--ov-ease), box-shadow .45s var(--ov-ease);
}
.ov-getaway__media { position: absolute; inset: 0; }
.ov-getaway__media img { width: 100%; height: 100%; object-fit: cover; }
.ov-gpanel__body { padding: 18px; }
.ov-getaway h3 { font-size: 19px; margin-bottom: 7px; }
.ov-getaway__desc { font-size: 13.5px; line-height: 1.6; color: var(--ov-body); margin-bottom: 10px; }
.ov-getaway__price { font-size: 13.5px; color: var(--ov-muted); }
.ov-getaway__price strong {
  font-family: var(--ov-font-display);
  font-size: 19px;
  color: var(--ov-violet-mid);
}

/* --------------------------------------------------------------------------
   21. FAQ
   -------------------------------------------------------------------------- */
.ov-faq { max-width: 860px; margin-inline: auto; }
.ov-faq__list { display: grid; gap: 12px; }

.ov-acc {
  position: relative;
  border-radius: var(--ov-r-md);
  overflow: hidden;
  box-shadow: var(--ov-sh-md);
  transition: transform .45s var(--ov-ease), box-shadow .45s var(--ov-ease);
}
.ov-acc__tint { position: absolute; inset: 0; background: var(--ov-tint-blue); }
.ov-acc--mint .ov-acc__tint { background: var(--ov-tint-mint); }

.ov-acc summary {
  position: relative;
  background: var(--ov-glass);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  border: 1px solid var(--ov-glass-brd);
  border-radius: var(--ov-r-md) var(--ov-r-md) 0 0;
  padding: 17px 20px;
  display: flex;
  justify-content: space-between;
  gap: 14px;
  align-items: center;
  font-size: 15.5px;
  color: var(--ov-ink);
}
.ov-acc:not([open]) summary { border-radius: var(--ov-r-md); }

.ov-acc__chev {
  flex: none;
  width: 9px; height: 9px;
  border-right: 2px solid var(--ov-violet);
  border-bottom: 2px solid var(--ov-violet);
  transform: rotate(-45deg);
  transition: transform .2s var(--ov-ease);
}
.ov-acc[open] .ov-acc__chev { transform: rotate(45deg); }

.ov-acc__body {
  position: relative;
  background: rgba(255, 255, 255, .42);
  padding: 0 20px 18px;
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--ov-body);
}

/* --------------------------------------------------------------------------
   22. Closing call to action
   -------------------------------------------------------------------------- */
.ov-join {
  position: relative;
  padding: var(--ov-section-y) var(--ov-gutter);
  overflow: hidden;
}
.ov-join__bg { position: absolute; inset: -14% 0; opacity: .9; }
.ov-join__bg img { width: 100%; height: 100%; object-fit: cover; }
.ov-join__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Was .7 to .9, which is a photograph you have to be told is there. The
     closing panel now carries a picture meant to be seen, so the veil only
     does what a veil is for: settle the top, and build toward the foot where
     the section hands over to the footer. */
  background: linear-gradient(180deg, rgba(238,234,251,.34), rgba(246,248,255,.56));
}
.ov-join__panel {
  position: relative;
  max-width: 1100px;
  margin-inline: auto;
  border-radius: var(--ov-r-2xl);
  overflow: hidden;
  box-shadow: var(--ov-sh-2xl);
}
/* Opaque once, which sealed the panel off from its own background: whatever
   the section was showing, the panel was a flat block of near-white. Sheer
   enough now to let the picture through, dense enough that the body's glass
   still has something calm to sit on. */
.ov-join__tint { position: absolute; inset: 0; background: rgba(248, 251, 255, .62); }
.ov-join__body {
  position: relative;
  background: rgba(255, 255, 255, .62);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
  border: 1px solid var(--ov-glass-brd-2);
  border-radius: var(--ov-r-2xl);
  padding: clamp(24px, 4vw, 42px);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(252px, 1fr));
  gap: 28px;
  align-items: center;
}
.ov-join__contact { font-size: 13px; line-height: 1.6; color: var(--ov-muted); }
.ov-join form { display: grid; gap: 12px; }

.ov-formnote {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ov-muted);
}
.ov-formstatus {
  font-size: 14px;
  padding: 12px 14px;
  border-radius: var(--ov-r-sm);
  background: rgba(14, 143, 134, .1);
  color: var(--ov-teal-dark);
}
.ov-formstatus--error {
  background: rgba(196, 47, 47, .1);
  color: #a52020;
}

/* --------------------------------------------------------------------------
   23. Footer
   -------------------------------------------------------------------------- */
.ov-footer {
  padding: 40px var(--ov-gutter) 46px;
  background: linear-gradient(180deg, rgba(255,255,255,0), rgba(255,255,255,.75));
}
.ov-footer__grid {
  max-width: var(--ov-max);
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(206px, 1fr));
  gap: 28px;
  font-size: 13.5px;
  line-height: 1.72;
  color: var(--ov-body-soft);
}
.ov-footer__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 21px;
  color: var(--ov-ink);
  margin-bottom: 10px;
}
.ov-footer__brand .ov-brand__mark { width: 62px; height: auto; }
.ov-footer__brand > img:only-child { width: 150px; height: auto; }
.ov-footer__about { margin-bottom: 14px; max-width: 34ch; }
.ov-footer__title { margin-bottom: 8px; color: var(--ov-teal-deep); }
.ov-footer ul { list-style: none; margin: 0; padding: 0; }

.ov-social { display: flex; gap: 10px; flex-wrap: wrap; }
.ov-social a {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  border-radius: var(--ov-r-sm);
  background: rgba(255, 255, 255, .78);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid var(--ov-glass-brd-2);
  color: var(--ov-link);
  box-shadow: 0 8px 18px rgba(17, 68, 112, .1);
  transition: transform .25s var(--ov-ease), color .2s;
}
.ov-social a:hover { transform: translateY(-3px); color: var(--ov-violet); }
.ov-social svg { width: 18px; height: 18px; fill: currentColor; }

.ov-footer__bottom {
  max-width: var(--ov-max);
  margin: 26px auto 0;
  padding-top: 18px;
  border-top: 1px solid rgba(17, 68, 112, .12);
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: space-between;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ov-muted);
}
.ov-footer__disclaimer { max-width: 64ch; }

/* --------------------------------------------------------------------------
   24. Inner pages (page.php, single.php, 404, legal)
   -------------------------------------------------------------------------- */
.ov-pagehead {
  padding: clamp(120px, 14vw, 170px) var(--ov-gutter) clamp(28px, 4vw, 46px);
  max-width: var(--ov-max);
  margin-inline: auto;
}
.ov-pagehead .ov-h2 { margin-bottom: 10px; }

.ov-prose {
  max-width: 72ch;
  font-size: 16px;
  line-height: 1.75;
  color: var(--ov-body);
}
.ov-prose > * + * { margin-top: 1.1em; }
.ov-prose h2 { font-size: clamp(22px, 2.6vw, 30px); margin-top: 1.8em; }
.ov-prose h3 { font-size: 20px; margin-top: 1.5em; }
.ov-prose ul,
.ov-prose ol { padding-left: 22px; display: grid; gap: 8px; }
.ov-prose img { border-radius: var(--ov-r-md); }
.ov-prose blockquote {
  margin: 0;
  padding: 16px 20px;
  border-left: 3px solid var(--ov-violet);
  background: rgba(255, 255, 255, .5);
  border-radius: 0 var(--ov-r-sm) var(--ov-r-sm) 0;
}
.ov-prose table { width: 100%; border-collapse: collapse; }
.ov-prose th,
.ov-prose td { padding: 10px 12px; border-bottom: 1px solid rgba(17, 68, 112, .12); text-align: left; }

.ov-postlist { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(268px, 1fr)); }
.ov-postcard h2 { font-size: 20px; margin-bottom: 8px; }
.ov-postcard__meta { font-size: 12.5px; color: var(--ov-muted); margin-bottom: 10px; }
.ov-postcard__thumb { border-radius: var(--ov-r-md); margin-bottom: 14px; overflow: hidden; }

.ov-pagination {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 32px;
}
.ov-pagination .page-numbers {
  display: grid;
  place-items: center;
  min-width: 42px; height: 42px;
  padding: 0 12px;
  border-radius: var(--ov-r-sm);
  background: rgba(255, 255, 255, .7);
  border: 1px solid var(--ov-glass-brd-2);
  color: var(--ov-ink);
  font-size: 14px;
}
.ov-pagination .page-numbers.current {
  background: var(--ov-violet);
  color: #fff;
  border-color: transparent;
}

/* Alignment classes inserted by the WordPress editor */
.alignleft  { float: left;  margin: 0 1.5em 1em 0; }
.alignright { float: right; margin: 0 0 1em 1.5em; }
.aligncenter { display: block; margin-inline: auto; }
.wp-caption-text { font-size: 13px; color: var(--ov-muted); margin-top: 6px; }

/* --------------------------------------------------------------------------
   25. Animations
   -------------------------------------------------------------------------- */
@keyframes ov-drift {
  0%   { transform: translate3d(0, 0, 0); }
  50%  { transform: translate3d(-2%, 1.4%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}
@keyframes ov-float {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-9px); }
  100% { transform: translateY(0); }
}
@keyframes ov-sheen {
  0%   { left: -40%; }
  60%  { left: 120%; }
  100% { left: 120%; }
}

/* Initial states for the entrance animations.
   The is-* classes are managed by assets/js/main.js. */
.ov-reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .75s var(--ov-ease), transform .75s var(--ov-ease);
}
.ov-reveal.is-visible { opacity: 1; transform: none; }

.ov-rise {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity .8s var(--ov-ease), transform .9s var(--ov-ease);
}
.ov-rise.is-visible { opacity: 1; transform: none; }

.ov-wipe {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 1.1s var(--ov-ease) 180ms;
}
.ov-wipe.is-visible { clip-path: inset(0 0 0 0); }

/* Without JS everything must still be visible */
.no-js .ov-reveal,
.no-js .ov-rise { opacity: 1; transform: none; }
.no-js .ov-wipe { clip-path: none; }

/* --------------------------------------------------------------------------
   26. Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 980px) {
  .ov-ppanel { grid-template-columns: 1fr; grid-template-rows: auto auto auto; }
  .ov-ppanel__text  { grid-area: 1 / 1 / 2 / 2; }
  .ov-ppanel__media { grid-area: 2 / 1 / 3 / 2; min-height: clamp(260px, 56vw, 380px); }
  .ov-pbar {
    grid-area: 3 / 1 / 4 / 2;
    flex-wrap: wrap;
    margin: clamp(12px, 2vw, 20px);
  }
  .ov-pbar__value { border-right: 0; padding-right: 0; }
  .ov-pbar__cta { width: 100%; justify-content: center; }
      }

@media (max-width: 860px) {
  .ov-burger {
    /* Flex with a gap, not a grid: on a grid inside a 44px box the rows
       stretched and the bars' centres ended up 15.4px apart, while the
       transform that folds them into an X assumes 6px. */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
  }
  .ov-burger span + span { margin-top: 0; }

  .ov-header__bar { flex-wrap: wrap; row-gap: 12px; }

  .ov-nav {
    display: none;
    order: 3;
    width: 100%;
    margin-left: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    padding-top: 8px;
    border-top: 1px solid rgba(17, 68, 112, .1);
  }
  .ov-nav.is-open { display: flex; }
  .ov-nav > li > a { display: block; padding: 10px 4px; }

  /* On mobile the submenu expands in flow instead of floating */
  .ov-nav .sub-menu {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    box-shadow: none;
    background: rgba(255, 255, 255, .55);
    padding: 10px 14px 14px;
    margin: 0 0 6px;
    display: none;
  }
  .ov-nav .menu-item-has-children.is-open > .sub-menu { display: grid; }

  .ov-header__cta { width: 100%; text-align: center; }
  .ov-header__cta { justify-content: center; }

    .ov-tours__head { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 560px) {
  :root { --ov-gutter: 16px; }
    .ov-join__body,
  .ov-band__body { padding: 22px; }
  .ov-footer__bottom { flex-direction: column; }
}

/* --------------------------------------------------------------------------
   27. Accessibility: reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
  .ov-reveal,
  .ov-rise { opacity: 1 !important; transform: none !important; }
  .ov-wipe { clip-path: none !important; }
}

/* --------------------------------------------------------------------------
   28. Print
   -------------------------------------------------------------------------- */
@media print {
  .ov-header, .ov-join, .ov-progress { display: none; }
  .ov-page { background: #fff; }
  * { box-shadow: none !important; backdrop-filter: none !important; }
}

/* ==========================================================================
   29. Glass and motion refinements
   Appended last so these rules win over the base ones above.
   ========================================================================== */

/* --------------------------------------------------------------------------
   29.1 Real glass over photography
   Every panel that sits on an image has an opaque tint underneath it as a
   fallback for browsers without backdrop-filter. That tint also blocks the
   photo completely, so the "glass" reads as a flat white box. Where the browser
   can actually blur, drop the tint and let the image come through.
   -------------------------------------------------------------------------- */
@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {

  .ov-band__tint,
  .ov-join__tint,
  .ov-tour__panel-tint,

  .ov-band__body,
  .ov-join__body {
    background: rgba(255, 255, 255, .42);
    -webkit-backdrop-filter: blur(30px) saturate(1.4);
    backdrop-filter: blur(30px) saturate(1.4);
    border: 1px solid rgba(255, 255, 255, .7);
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, .85),
      inset 0 -1px 0 rgba(255, 255, 255, .25);
  }

  /* Properly transparent glass on the search card: 20% white over a heavy blur,
     so the photograph reads through it instead of being hidden by a frosted
     panel. Measured against the blurred pool behind it, rgb(148,193,225), the
     old label colour #5c7f99 never reached 4.5:1 at any opacity — not even at
     66% — so the labels darken to #1e4359, which clears it at 6.3:1 even this
     transparent. The form fields stay opaque white and anchor the whole thing. */
  .ov-searchbox__tint { opacity: 0; }
  .ov-searchbox__body {
    background: rgba(255, 255, 255, .20);
    -webkit-backdrop-filter: blur(40px) saturate(1.7);
    backdrop-filter: blur(40px) saturate(1.7);
    border-color: rgba(255, 255, 255, .55);
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, .75),
      inset 0 -1px 0 rgba(255, 255, 255, .22),
      0 26px 60px rgba(17, 68, 112, .26);
  }
  .ov-searchbox__body .ov-field { color: #1e4359; }
  .ov-searchbox__body .ov-tab:not(.is-active) { color: #1e4359; }

  .ov-tour__panel-body,
  .ov-gpanel__body {
    background: rgba(255, 255, 255, .5);
    -webkit-backdrop-filter: blur(24px) saturate(1.35);
    backdrop-filter: blur(24px) saturate(1.35);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .8);
  }

  /* The arrow discs sit on the dark wash at the foot of a photograph, so there
     is something behind them worth showing. White type on 22% white over that
     wash measures about 4.8:1. */
  .ov-tcard__go {
    background: rgba(255, 255, 255, .22);
    -webkit-backdrop-filter: blur(18px) saturate(1.4);
    backdrop-filter: blur(18px) saturate(1.4);
    border: 1px solid rgba(255, 255, 255, .45);
    color: #fff;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3);
  }

  /* Header: heavier blur now that it can be seen against the hero photo. */
  .ov-header__bar {
    -webkit-backdrop-filter: blur(34px) saturate(1.35);
    backdrop-filter: blur(34px) saturate(1.35);
  }
}

/* --------------------------------------------------------------------------
   29.2 Slow drift on background photography (Ken Burns)
   Applied to the <img>, never to the [data-parallax] layer, because the
   parallax script writes transform on that element every frame.
   -------------------------------------------------------------------------- */
@keyframes ov-kenburns {
  from { transform: scale(1.02) translate3d(0, 0, 0); }
  to   { transform: scale(1.1) translate3d(-1.6%, -1.2%, 0); }
}

/* --------------------------------------------------------------------------
   29.3 Softer entrance
   A touch of scale on top of the rise reads as depth rather than a slide.
   --ov-delay is set per element by main.js so grid children arrive in sequence.
   -------------------------------------------------------------------------- */
.ov-reveal {
  transform: translateY(26px) scale(.985);
  transition:
    opacity .85s var(--ov-ease) var(--ov-delay, 0ms),
    transform .95s var(--ov-ease) var(--ov-delay, 0ms);
}
.ov-reveal.is-visible { transform: none; }

/* --------------------------------------------------------------------------
   29.4 Cursor glare on cards
   --mx / --my follow the pointer, set by main.js.
   -------------------------------------------------------------------------- */
.ov-tcard,
.ov-getaway,
.ov-bcard--plain,
.ov-step3 { isolation: isolate; }

.ov-tcard::after,
.ov-getaway::after,
.ov-bcard--plain::after,
.ov-step3::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(
    240px circle at var(--mx, 50%) var(--my, 0%),
    rgba(255, 255, 255, .38),
    rgba(255, 255, 255, .08) 45%,
    transparent 70%);
  opacity: 0;
  transition: opacity .4s var(--ov-ease);
}

/* Keep the readable content above the glare. The pseudo-element is the last
   child, so without an explicit stacking order it would paint over the text. */
.ov-tcard__body,
.ov-tcard__badge,
.ov-gpanel { z-index: 3; }
.ov-bcard--plain > * { position: relative; z-index: 3; }

/* The photo bcards need the same lift, for a second reason: their picture is
   raised to z-index 1 so it paints over the loading shimmer behind it, and
   that also puts it over this in-flow sibling. Without a stacking order of its
   own the wash and its type disappear under the photograph. */
.ov-bcard--photo .ov-bcard__body { z-index: 3; }

@media (hover: hover) and (pointer: fine) {
  .ov-tcard:hover::after,
  .ov-getaway:hover::after,
  .ov-bcard--plain:hover::after,
  .ov-step3:hover::after { opacity: 1; }
}

/* --------------------------------------------------------------------------
   29.5 Section headings: an accent rule that draws itself in
   -------------------------------------------------------------------------- */
.ov-eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
}
.ov-eyebrow::after {
  content: "";
  flex: none;
  width: 56px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--ov-teal), transparent);
  transition: width 1s var(--ov-ease) .2s;
}
/* Collapsed only while its section is still waiting to be revealed, so an
   eyebrow outside a .ov-reveal is never left with an invisible rule. */
.ov-reveal:not(.is-visible) .ov-eyebrow::after { width: 0; }

/* --------------------------------------------------------------------------
   29.6 Buttons and links
   -------------------------------------------------------------------------- */
.ov-btn--primary { transition: transform .35s var(--ov-ease), box-shadow .35s var(--ov-ease), background-color .25s; }
@media (hover: hover) and (pointer: fine) {
  .ov-btn--primary:hover { transform: translateY(-2px); box-shadow: 0 22px 42px rgba(30, 14, 90, .42); }
  .ov-btn--ghost:hover   { transform: translateY(-2px); }
  .ov-social a:hover     { transform: translateY(-3px) scale(1.06); }
}

/* Underline that grows from the left on footer and prose links. */
.ov-footer a:not(.ov-social a),
.ov-prose a {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size .35s var(--ov-ease), color .2s;
}
.ov-footer a:not(.ov-social a):hover,
.ov-prose a:hover { background-size: 100% 1px; }

/* --------------------------------------------------------------------------
   29.7 Reduced motion wins over all of the above
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ov-eyebrow::after { width: 56px; transition: none; }
  .ov-tcard::after,
  .ov-getaway::after,
  .ov-bcard--plain::after,
  .ov-step3::after { display: none; }
}

/* ==========================================================================
   30. The dark slab
   One deep section in the middle of the page. Thirteen sections on the same
   pale wash gave the page no rhythm; a single dark moment gives the eye
   somewhere to land and makes the pale sections read as deliberate.

   Built as a rounded slab inside the container rather than a full-bleed band,
   so it never meets the page gradient at a hard edge.
   ========================================================================== */


/* 30.1 The logo mark as a watermark.
   The brand has a distinctive symbol that otherwise only ever appears at 48px
   in the header. At this size it becomes texture instead of a logo. */


/* 30.3 List bullets drawn from the mark */


/* 30.4 Glass inverted: light film on dark, not dark film on light */


/* 30.5 The proposition at the size it deserves.
   "$1 a night" is the entire offer and it was living in a 23px floating chip. */


/* 30.6 Focus ring has to be visible on the dark ground too */


/* ==========================================================================
   31. Wave edges on the photographic sections
   ========================================================================== */

/* --------------------------------------------------------------------------
   31.2 Wave edges on the photographic sections
   The image used to meet the page gradient along a straight line. A stretched
   SVG mask carves both edges at once, so there is no second colour to match —
   which is what made the old hero seam visible in the first place.

   preserveAspectRatio="none" plus 100% 100% sizing means one path covers every
   section height; the amplitude scales with the section.
   -------------------------------------------------------------------------- */
.ov-scene {
  position: absolute;
  inset: 0;
  overflow: hidden;
  -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 600' preserveAspectRatio='none'%3E%3Cpath d='M0,64 C240,8 480,96 720,52 C960,8 1200,86 1440,26 L1440,562 C1200,600 960,524 720,568 C480,612 240,538 0,582 Z' fill='%23000'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 600' preserveAspectRatio='none'%3E%3Cpath d='M0,64 C240,8 480,96 720,52 C960,8 1200,86 1440,26 L1440,562 C1200,600 960,524 720,568 C480,612 240,538 0,582 Z' fill='%23000'/%3E%3C/svg%3E");
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* The sections need room for the crests, or the wave eats into the panel. */
.ov-band,
.ov-join { padding-block: clamp(64px, 8vw, 120px); }

/* On short viewports the amplitude gets disproportionate, so flatten it. */
@media (max-width: 640px) {
  .ov-scene {
    -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 600' preserveAspectRatio='none'%3E%3Cpath d='M0,34 C360,4 720,52 1080,20 C1260,6 1350,14 1440,10 L1440,580 C1080,600 720,556 360,584 C240,592 120,590 0,592 Z' fill='%23000'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 600' preserveAspectRatio='none'%3E%3Cpath d='M0,34 C360,4 720,52 1080,20 C1260,6 1350,14 1440,10 L1440,580 C1080,600 720,556 360,584 C240,592 120,590 0,592 Z' fill='%23000'/%3E%3C/svg%3E");
  }
}

@media (max-width: 782px) {
}

/* --------------------------------------------------------------------------
   33. Hero wave on narrow screens
   The mask stretches a 1440-wide viewBox down to ~380px, so the six-crest
   desktop wave compresses into a ripple. One broad swell instead.
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
}

/* ==========================================================================
   34. The plane
   One per section, all reading their height, speed and start from custom
   properties. The delays are spread across the page so scrolling rarely puts
   two on screen together: one aircraft passing is a detail, several at once is
   air traffic.
   ========================================================================== */
.ov-section { position: relative; }
.ov-section { overflow-x: clip; }

.ov-plane {
  position: absolute;
  top: var(--ov-plane-top, 20%);
  left: 0;
  z-index: 2;
  display: block;
  width: clamp(18px, 2vw, 28px);
  pointer-events: none;
  opacity: 0;
  will-change: transform;
  animation: ov-fly var(--ov-plane-dur, 24s) linear infinite var(--ov-plane-delay, 0s);
}
.ov-plane svg { width: 100%; height: auto; display: block; }

/* Over photography, and over the pale sections where a white plane would
   simply disappear. */
.ov-plane--light { color: rgba(255, 255, 255, .92); filter: drop-shadow(0 2px 7px rgba(8, 39, 66, .3)); }
.ov-plane--ink   { color: rgba(91, 63, 214, .34); }

.ov-plane__trail {
  position: absolute;
  right: 88%;
  top: 52%;
  width: clamp(60px, 8vw, 130px);
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(270deg, currentColor, transparent);
  opacity: .5;
}

/* Crosses across 68% of the cycle rather than 42%. Slowing the aircraft by
   lengthening the cycle alone would have lengthened the wait with it; giving
   the crossing a bigger share of a slightly longer cycle slows the flight and
   leaves the pause about where it was. */
@keyframes ov-fly {
  0%   { transform: translate3d(-14vw, 3vh, 0); opacity: 0; }
  4%   { opacity: 1; }
  62%  { opacity: 1; }
  68%  { transform: translate3d(114vw, -4vh, 0); opacity: 0; }
  100% { transform: translate3d(114vw, -4vh, 0); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .ov-plane { display: none; }
}

/* ==========================================================================
   35. The school
   The brand mark is two tapered fish. These are the same creature at a size
   and pace where they register as movement in the water rather than as
   illustration: three of them, all different speeds, none in a hurry.
   ========================================================================== */
.ov-school {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
  /* Makes cqw below resolve against the slab. Travel was written in vw, the
     viewport's width, so on a wide screen the fish spent half of every cycle
     outside a box only 1180px across. */
  container-type: inline-size;
}

.ov-fish {
  position: absolute;
  left: 0;
  display: block;
  /* Was #7fe6da at .34, picked against a navy slab. On cream a pale turquoise
     vanishes. Measured against #f7f4ec, this teal reads 1.36:1 at the opacities
     below — the earlier pass sat at 1.27:1 and the fish looked like lint. */
  color: #0e8f86;
  will-change: transform;
}
.ov-fish__bob { display: block; animation: ov-bob 7s ease-in-out infinite; }
.ov-fish svg { display: block; width: 100%; height: auto; }

/* Different size, depth, speed and start, so they never form a row. */
/* Opacity roughly doubled. Turquoise at 16% over navy is a luminance
   difference of about 1.3:1 — present in the DOM, invisible to a person. */
.ov-fish--1 { top: 22%; width: 66px; opacity: .26; animation: ov-swim 40s linear infinite; }
.ov-fish--2 { top: 56%; width: 42px; opacity: .28; animation: ov-swim 58s linear infinite -20s; color: #6b5bd6; }
.ov-fish--3 { top: 78%; width: 28px; opacity: .30; animation: ov-swim 76s linear infinite -46s; }
.ov-fish--2 .ov-fish__bob { animation-duration: 9s; }
.ov-fish--3 .ov-fish__bob { animation-duration: 11s; }

@keyframes ov-swim {
  from { transform: translate3d(-14%, 0, 0); }
  to   { transform: translate3d(104cqw, 0, 0); }
}
@keyframes ov-bob {
  0%   { transform: translateY(0) rotate(0deg); }
  50%  { transform: translateY(-9px) rotate(-3deg); }
  100% { transform: translateY(0) rotate(0deg); }
}

@media (prefers-reduced-motion: reduce) {
  .ov-school { display: none; }
}

/* ==========================================================================
   36. Section background photograph
   One definition shared by every section that wants one. Washed to texture:
   the mask keeps it clear of the heading on the left where dark type has to
   stay legible, and desaturation stops a photograph's own colours arguing with
   the page palette.
   ========================================================================== */
.ov-section--bg { position: relative; overflow: hidden; }
.ov-section--bg > .ov-wrap { position: relative; z-index: 1; }


/* ==========================================================================
   37. Phones
   Scoped to one breakpoint and appended last, so it can only add to the
   desktop layout, never alter it. Four themes:

     - washes turn vertical. The card washes are angled at 94deg, which puts
       their dark end under the text on a wide card. Narrow that card to a
       phone and the same angle runs across the photograph instead, leaving
       white type over whatever the picture happens to be bright at.
     - a floor under type. Fifteen elements sat between 11.5px and 12.5px:
       a reasonable caption on a desktop, a squint on a phone.
     - tap targets. Twenty-three links were under 40px tall, some 17px —
       a link you aim at rather than press.
     - reading order. The membership photograph came before its own heading.
   ========================================================================== */
@media (max-width: 600px) {

  /* 37.1 The photo cards' bottom-up wash is now the rule at every width, so
     there is nothing left to correct here. */
  /* The chip's margin-top:auto was eating all the free space, so only it moved
     down and the label stayed up in the bright part. */
  .ov-bcard--photo .ov-bcard__chip { margin-top: 14px; }
  .ov-tcard__body {
    background: linear-gradient(to top,
      rgba(6, 26, 48, .95) 0%,
      rgba(6, 26, 48, .84) 38%,
      rgba(6, 26, 48, .42) 68%,
      rgba(6, 26, 48, .06) 100%);
  }
  .ov-story-hero__body {
    background: linear-gradient(to top,
      rgba(6, 26, 48, .95) 0%,
      rgba(6, 26, 48, .86) 40%,
      rgba(6, 26, 48, .5) 72%,
      rgba(6, 26, 48, .1) 100%);
  }

  /* 37.2 Nothing under 13px */
  .ov-eyebrow,
  .ov-field__label,
  .ov-stat__label,
  .ov-story__meta,
  .ov-stories__striptitle,
  .ov-bcard__label,
  .ov-figure__label,
  .ov-rate__points,
  .ov-rate__property,
  .ov-pstats__label,
  .ov-pcard__when,
  .ov-pbar__note,
  .ov-pfeat__note,
  .ov-trustbar__top,
  .ov-trustbar__sub,
  .ov-step3__text,
  .ov-tcard__meta { font-size: 13px; }
  .ov-eyebrow { letter-spacing: .14em; }

  /* 37.3 Something you can actually hit */
  .ov-footer a,
  .ov-contactline a,
  .ov-textlink {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
  }
  .ov-nav a { min-height: 44px; display: inline-flex; align-items: center; }
  .ov-tcard__go { min-width: 42px; min-height: 42px; }
  /* A card title that is also the link. Padding rather than min-height, so a
     title that wraps to two lines still grows instead of being boxed. */
  .ov-gpanel__body h3 a { display: inline-block; padding-block: 9px; }
  .ov-textlink { min-height: 44px; }
  .ov-footer__disclaimer { font-size: 13px; }
  /* Links sitting inside a running sentence keep their line box: forcing 40px
     on them would open gaps mid-paragraph. WCAG 2.5.8 exempts them for
     exactly this reason. */
  .ov-btn { min-height: 48px; }
  .ov-field { padding-block: 12px; }
  .ov-field input,
  .ov-field select,
  .ov-field textarea {
    /* 16px, or iOS zooms the page in on focus and leaves it there. */
    font-size: 16px;
    min-height: 26px;
  }

  /* 37.4 The heading arrives before the picture of the thing */
  /* order alone. Switching .ov-mem to flex also reordered it, but its
     align-items: center then stopped the photograph stretching and the
     section grew by a thousand pixels. Grid honours order too. */
  .ov-mem__copy  { order: 1; }
  .ov-mem__media { order: 2; }

  /* 37.5 The step keeps all three of its columns, just tighter. Dropping to
     two left the third child with nowhere to go but a new row, which added a
     thousand pixels to the section. */
  .ov-step2 { grid-template-columns: 38px 40px minmax(0, 1fr); column-gap: 10px; }
  .ov-step2:not(:last-child)::before { left: 19px; }
  .ov-step2__badge { width: 40px; height: 40px; }
  .ov-chiprow li { font-size: 13px; }
}

/* ==========================================================================
   8. Marketing front page
   Added when the site moved from travel to marketing. Everything above still
   serves the Travel page; nothing here is used by it except the shared tokens.
   ========================================================================== */

/* --------------------------------------------------------------------------
   8.1 Shared furniture
   -------------------------------------------------------------------------- */
.ov-front .ov-wrap,
.ov-pagehead .ov-wrap { max-width: var(--ov-max-wide); padding-inline: clamp(24px, 4vw, 104px); }

.ov-lede {
  max-width: 56ch;
  font-size: 16px;
  line-height: 1.7;
  color: var(--ov-lead);
  text-wrap: pretty;
}
.ov-lede--wide { max-width: 62ch; }

.ov-head { margin-bottom: clamp(34px, 4.4vw, 58px); }
.ov-head .ov-h2 { max-width: 20ch; }
.ov-head .ov-lede { margin-top: 18px; }

/* Head variants. --split puts the lede in a right-hand column against the
   title; --row is the same idea with a button instead of prose. */
.ov-head--row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(24px, 4vw, 64px);
  flex-wrap: wrap;
}
.ov-kicker {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ov-muted);
}

.ov-label {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ov-muted);
}

.ov-delta {
  font-size: 12px;
  font-weight: 700;
  color: var(--ov-coral);
}

.ov-chip {
  margin: 0;
  width: max-content;
  max-width: 100%;
  font-size: 12.5px;
  font-weight: 600;
  border-radius: var(--ov-r-pill);
}
.ov-chip--ink {
  padding: 14px 20px;
  color: #fff;
  background: rgba(27, 16, 53, .92);
  box-shadow: 0 22px 46px -28px rgba(27, 16, 53, .9);
}
.ov-chip--glass {
  padding: 16px 22px;
  border-radius: var(--ov-r-lg);
  font-weight: 500;
  line-height: 1.5;
  color: var(--ov-body-soft);
  background: var(--ov-glass);
  backdrop-filter: var(--ov-blur);
  border: 1px solid var(--ov-glass-brd);
}

.ov-btn__arrow { transition: transform .3s var(--ov-ease); }
.ov-btn:hover .ov-btn__arrow,
.ov-bento__band:hover .ov-btn__arrow,
.ov-tier:hover .ov-btn__arrow { transform: translateX(4px); }

.ov-btn--underline {
  padding: 0 0 4px;
  min-height: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ov-body-soft);
  background: none;
  border: 0;
  border-bottom: 1.5px solid rgba(91, 49, 214, .3);
  border-radius: 0;
}
.ov-btn--underline:hover { color: var(--ov-violet); border-bottom-color: var(--ov-violet); }

.screen-reader-text {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Drifting colour fields. Three overlapping radial gradients, no image. */
.ov-aura {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}
.ov-aura--violet {
  left: 60px; top: 0;
  width: 760px; height: 640px;
  background: radial-gradient(circle, rgba(139, 92, 246, .34), transparent 68%);
  animation: ovDrift 15s ease-in-out infinite;
}
.ov-aura--blue {
  right: 90px; top: 240px;
  width: 680px; height: 600px;
  background: radial-gradient(circle, rgba(59, 130, 246, .28), transparent 68%);
  animation: ovDrift2 18s ease-in-out infinite;
}
.ov-aura--coral {
  left: 32%; top: 300px;
  width: 560px; height: 500px;
  background: radial-gradient(circle, rgba(255, 122, 107, .24), transparent 68%);
  animation: ovDrift 20s ease-in-out infinite;
}

@keyframes ovDrift {
  0%, 100% { transform: none; }
  50%      { transform: translate3d(40px, -30px, 0) scale(1.06); }
}
@keyframes ovDrift2 {
  0%, 100% { transform: none; }
  50%      { transform: translate3d(-46px, 34px, 0) scale(1.04); }
}
@keyframes ovPulse {
  0%, 100% { transform: scale(1);   opacity: 1; }
  50%      { transform: scale(1.5); opacity: .55; }
}
@keyframes ovDraw { from { stroke-dashoffset: var(--ov-dash, 2600); } to { stroke-dashoffset: 0; } }
/* The distance is one pass, measured in marquee() — see main.js. The 50%
   fallback is what two printed passes come to, which is what runs if the
   script never arrives. */
@keyframes ovMarquee {
  from { transform: none; }
  to   { transform: translate3d(calc(-1 * var(--ov-marq-shift, 50%)), 0, 0); }
}

/* --------------------------------------------------------------------------
   8.2 Hero
   -------------------------------------------------------------------------- */
.ov-hero {
  position: relative;
  /* The tint used to live only under the growth curve, so the top half of the
     hero sat plain and the curve's own edge read as the boundary of the
     background. The wash now comes down from the top as well, and the curve
     is a line drawn on a tinted ground rather than the lid of it. */
  /* No background of its own. Section 19 lays a single ground on the page
     wrapper and makes this one transparent, which is what stopped the seam
     under the header — anything painted here is overridden and only misleads
     the next person to look. */
  overflow: hidden;
  padding-bottom: clamp(60px, 6.8vw, 100px);
}
.ov-hero__aura { position: absolute; inset: -220px; pointer-events: none; }

.ov-hero__inner {
  position: relative;
  z-index: 2;
  max-width: var(--ov-max-wide);
  margin-inline: auto;
  padding: clamp(48px, 5.6vw, 78px) clamp(24px, 4vw, 104px) 0;
}

/* The photograph behind the hero.

   Full bleed to the right and top, faded out to the page's own ground before
   it reaches the copy — the headline sits on paper, and the picture is what
   the reader looks past it into. Fading rather than cropping is what keeps it
   from reading as a panel bolted to one side of the section. */
.ov-hero__photo {
  position: absolute;
  /* Started at the section's left edge, the picture put the presenter and the
     screen straight behind the headline. Inset, it keeps to the half of the
     page that is its own, and the copy sits on the page's own ground. */
  inset: 0 0 0 12%;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.ov-hero__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 50%;
  /* Pushed right so the presenter and the screen clear the headline. Scaled
     first, and by more than the shift: translated on its own the picture left
     its own left edge sitting inside the fade as a straight line. Enlarged, it
     still fills the frame and there is no edge to expose. */
  transform: scale(1.14) translate(5%, 4%);
}
/* The picture fades out, rather than being covered up.

   A veil painting flat --ov-bg over its left third looked identical to the
   ground on a plain page and drew a straight vertical line down this one,
   because the ground here has the drifting colour fields on it and the veil
   did not. Masking the photograph lets those fields show through it instead,
   and there is no edge left to see. */
.ov-hero__photo {
  /* Two fades, intersected: one across and one down. The horizontal one alone
     still let the picture reach the top of the section, where it met the
     header as a horizon, and it began soon enough that the onset itself read
     as an edge. Starting fully clear and taking longer to arrive, there is no
     point at which the photograph can be said to start. */
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0 38%, rgba(0, 0, 0, .4) 54%, #000 70%),
    linear-gradient(180deg, transparent 0, rgba(0, 0, 0, .45) 14%, #000 34%);
          mask-image:
    linear-gradient(90deg, transparent 0 38%, rgba(0, 0, 0, .4) 54%, #000 70%),
    linear-gradient(180deg, transparent 0, rgba(0, 0, 0, .45) 14%, #000 34%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

/* Nothing left to veil: the fades above do the work, and a wash painted on top
   of them was flat colour over a ground that has drifting fields on it — which
   is what drew the vertical line down the page the first time. */
.ov-hero__veil { display: none; }

/* Without a picture the section keeps its old pale ground and nothing below
   has to know the difference. */
.ov-hero:not(.ov-hero--photo) .ov-hero__photo { display: none; }

/* Colour fields over the photograph, under the curve.

   The page's own drifting fields live behind everything, so across the half of
   the section the picture covers they were doing nothing at all. These put the
   same light back on top of it.

   Plain alpha, not a blend mode: screen was the instinct — it only ever adds
   light — but this photograph is a bright room, and adding light to something
   already near white does nothing at all. Measured, the layer was invisible. */
.ov-hero__orbs {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}
.ov-orb {
  position: absolute;
  border-radius: 50%;
  /* Same reasoning as .ov-aura: no filter, own layer. */
  will-change: transform;
}
.ov-orb--violet {
  left: 30%; top: -14%;
  width: 46%; height: 62%;
  background: radial-gradient(circle, rgba(91, 49, 214, .34), transparent 66%);
  animation: ovDrift 17s ease-in-out infinite;
}
.ov-orb--blue {
  right: 4%; top: 6%;
  width: 38%; height: 54%;
  background: radial-gradient(circle, rgba(59, 130, 246, .3), transparent 66%);
  animation: ovDrift2 21s ease-in-out infinite;
}
.ov-orb--coral {
  left: 44%; bottom: -18%;
  width: 40%; height: 58%;
  background: radial-gradient(circle, rgba(255, 122, 107, .26), transparent 66%);
  animation: ovDrift 24s ease-in-out infinite;
}
.ov-orb--pale {
  right: 26%; bottom: 2%;
  width: 30%; height: 42%;
  background: radial-gradient(circle, rgba(196, 181, 253, .34), transparent 68%);
  animation: ovDrift2 19s ease-in-out infinite;
}
.ov-orb--deep {
  left: 58%; top: 26%;
  width: 26%; height: 36%;
  background: radial-gradient(circle, rgba(58, 43, 214, .24), transparent 68%);
  animation: ovDrift 27s ease-in-out infinite;
}

/* Two tighter ones. The wide fields above melt into a single wash — these stop
   soon enough to read as discs, which is what the eye actually recognises as
   the page's own pattern. */
.ov-orb--disc-a {
  left: 40%; top: 48%;
  width: 15%; height: 21%;
  background: radial-gradient(circle, rgba(139, 92, 246, .3), transparent 52%);
  animation: ovDrift2 23s ease-in-out infinite;
}
.ov-orb--disc-b {
  right: 15%; top: 14%;
  width: 12%; height: 17%;
  background: radial-gradient(circle, rgba(255, 122, 107, .24), transparent 52%);
  animation: ovDrift 20s ease-in-out infinite;
}

/* They drift; under reduced motion they simply sit still. */
@media (prefers-reduced-motion: reduce) {
  .ov-orb { animation: none; }
}

/* The climb. Held to its own ratio in the right-hand column: drawn at section
   width with preserveAspectRatio="none" it flattened into a diagonal and the
   cards had nothing to point at. */
.ov-hero__chart {
  position: relative;
  z-index: 2;
  aspect-ratio: 1010 / 610;
  margin-top: clamp(8px, 1.6vw, 26px);
}
.ov-chart {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.ov-chart__line,
.ov-chart__halo {
  /* The curve's real length, measured. A dasharray longer than the path leaves
     the browser drawing a pattern nobody asked for. */
  stroke-dasharray: 1180;
  animation: ovDraw 2.2s cubic-bezier(.3, .7, .2, 1) .3s both;
}
.ov-chart__halo  { opacity: .55; }
.ov-chart__links { animation: ovFadeIn .8s ease-out 2.1s both; }
.ov-chart__nodes,
.ov-chart__cores { animation: ovFadeIn .8s ease-out 2s both; }

/* White discs, so a node reads as light sitting on the photograph rather than
   as a hole punched through it. */
.ov-chart__nodes circle {
  fill: #fff;
  filter: drop-shadow(0 0 12px rgba(124, 92, 245, .75));
}
.ov-chart__end { filter: drop-shadow(0 0 16px rgba(255, 77, 54, .85)); }

@keyframes ovFadeIn { from { opacity: 0; } to { opacity: 1; } }

.ov-hero__grid {
  position: relative;
  z-index: 2;
  display: grid;
  /* The chart is the wider half now that it is a drawing rather than a
     backdrop; the copy holds a comfortable measure at three lines. */
  /* minmax(0, …) on purpose: a bare 1fr never shrinks below the headline's
     min-content width, so the copy column quietly stole 260px from the chart
     and the ratio typed here did nothing at all. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.16fr);
  gap: clamp(28px, 3.4vw, 44px);
  align-items: start;
}
.ov-hero__copy { position: relative; padding-top: 20px; }

.ov-front .ov-h1,
.ov-h1--page {
  /* 90px was sized for a headline that had the width of the page to itself.
     Beside a chart it has a column, and three lines at 72 read better than
     two enormous ones that crowd the drawing. */
  font-size: clamp(38px, 4.6vw, 72px);
  /* Filled greedily, not balanced. text-wrap: balance evens the three lines
     out, which puts "move" at the end of line two and leaves the underline
     hanging off the edge of the measure. ("normal" is not a value this
     property takes — it was dropped, and the balance quietly stayed.) */
  text-wrap: wrap;
  line-height: .9;
  letter-spacing: -.05em;
  color: var(--ov-ink);
}

/* The highlighted word and its hand-drawn rule. The rule is a child of the
   word rather than a background, so it tracks the word when the line wraps. */
.ov-mark {
  position: relative;
  white-space: nowrap;
  color: var(--ov-violet);
}
/* Contains the rule's negative z-index, so it goes behind the headline's own
   letters and no further. */
.ov-front .ov-h1,
.ov-h1--page { isolation: isolate; }
/* The headline is set at line-height .9, so its line boxes are shorter than
   the letters and an underline pinned under the word reaches into the line
   below — by 29px here. The design lives with that because its wrap leaves
   "move" at the start of a line, where the rule only grazes the gap to the
   left of the next word. Ours wraps with "move" at the end of the line, so the
   same rule cut straight through "businesses".
   Rather than fight the wrap, the stroke is drawn behind the text: it tucks
   under whatever follows instead of crossing it, at any width. */
.ov-mark__rule {
  position: absolute;
  left: 2%; right: 2%; bottom: -.02em;
  width: 96%; height: .16em;
  z-index: -1;
  overflow: visible;
  color: var(--ov-coral);
}
/* The drawn underline waits for its heading. Every one of these below the
   fold had already finished by the time it came into view. */
.ov-mark__rule path {
  stroke-dasharray: 260;
  stroke-dashoffset: 260;
  --ov-dash: 260;
}
.is-visible .ov-mark__rule path,
.ov-rise.is-visible .ov-mark__rule path {
  animation: ovDraw 1.1s cubic-bezier(.3, .7, .2, 1) 1.1s both;
}

.ov-hero__copy .ov-pill { margin-bottom: 20px; }
.ov-hero__copy .ov-h1 { margin-bottom: 18px; }

.ov-hero__sub {
  margin: 32px 0 0;
  max-width: 440px;
  font-size: 16.5px;
  line-height: 1.7;
  color: var(--ov-lead);
  text-wrap: pretty;
}

.ov-hero__cta {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  margin-top: 38px;
}
.ov-hero__cta .ov-btn--primary {
  padding: 17px 32px;
  border-radius: 16px;
  font-size: 14.5px;
  font-weight: 700;
  box-shadow: var(--ov-sh-violet);
}

/* Floating cards. A layer over the chart rather than a box of its own: every
   card is placed in percentages of the same area the curve is drawn in, so a
   card and the node it names stay together at any width. */
.ov-hero__floats { position: absolute; inset: 0; }
.ov-hero__floats > * { position: absolute; }

.ov-card--signal {
  left: 41.8%; top: 1.6%;
  /* Wide enough for "Growth signal" and its delta to share one line: broken
     over two, the label stops being a label and starts being a sentence. */
  width: clamp(244px, 31%, 280px);
  padding: 24px;
  border-radius: var(--ov-r-xl);
  overflow: visible;
  background: rgba(255, 255, 255, .66);
  backdrop-filter: blur(26px) saturate(1.3);
  border: 1px solid var(--ov-glass-brd-2);
  box-shadow: 0 40px 80px -50px rgba(91, 49, 214, .7);
}
.ov-card__top { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.ov-card__big {
  margin: 18px 0 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 52px;
  line-height: 1;
  letter-spacing: -.045em;
  color: var(--ov-ink);
}
.ov-card__note {
  margin: 10px 0 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ov-body-soft);
}

.ov-hero__floats .ov-note { left: 59.4%; top: 64.8%; }

/* The acquisition-cost card: heading, figure, then a line of plain English.
   "CAC ↓ 34%" on its own means nothing to a reader who does not already work
   in marketing, which is most of the people this page is for. */
.ov-card--cac {
  left: 17.8%; top: 36.7%;
  width: clamp(170px, 21%, 196px);
  padding: 16px 18px;
  border-radius: var(--ov-r-lg);
  background: rgba(255, 255, 255, .86);
  backdrop-filter: blur(20px) saturate(1.25);
  border: 1px solid rgba(255, 255, 255, .9);
  box-shadow: 0 26px 54px -34px rgba(76, 46, 168, .6);
}
.ov-card__head {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  font-weight: 700;
  font-size: 14.5px;
  color: var(--ov-ink);
}
.ov-card__head .ov-note__badge { width: 30px; height: 30px; border-radius: 9px; }
.ov-card__head .ov-icon { width: 16px; height: 16px; }
.ov-card--cac .ov-note__badge { color: var(--ov-violet); background: rgba(237, 233, 254, .95); }
.ov-card__mid {
  margin: 8px 0 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 27px;
  line-height: 1.05;
  letter-spacing: -.03em;
  color: var(--ov-violet);
}

/* The small cards pinned to the curve. Icon, figure, one line — read in
   passing, never stopped at, which is why the label never wraps past two. */
.ov-note {
  display: flex;
  align-items: center;
  gap: 11px;
  width: max-content;
  max-width: 210px;
  padding: 13px 16px 13px 13px;
  border-radius: 16px;
  background: rgba(255, 255, 255, .82);
  backdrop-filter: blur(18px) saturate(1.25);
  border: 1px solid rgba(255, 255, 255, .9);
  box-shadow: 0 18px 40px -26px rgba(76, 46, 168, .55);
}
.ov-note__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 12px;
}
.ov-note__badge .ov-icon { width: 19px; height: 19px; }
.ov-note--violet .ov-note__badge { color: var(--ov-violet); background: rgba(237, 233, 254, .95); }
.ov-note--coral  .ov-note__badge { color: #f2624f;         background: rgba(255, 228, 223, .95); }

.ov-note__text { display: grid; gap: 2px; }
.ov-note__num {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 19px;
  line-height: 1.1;
  letter-spacing: -.02em;
  color: var(--ov-ink);
}
.ov-note__label {
  font-size: 12px;
  line-height: 1.35;
  color: var(--ov-body-soft);
}

/* The coral full stop that closes the headline. */
.ov-h1__dot { color: var(--ov-coral); }


/* One panel, floating on the photograph. The figures, the brand line and the
   sentence that says what the line means were three separate bands; over a
   picture they read as three unrelated pieces of furniture rather than as one
   statement. */
.ov-hero__panel {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: auto minmax(180px, max-content) minmax(200px, 1fr);
  align-items: center;
  gap: clamp(20px, 3vw, 46px);
  margin-top: clamp(30px, 4vw, 58px);
  padding: clamp(18px, 2.2vw, 30px) clamp(20px, 2.6vw, 38px);
  border-radius: clamp(18px, 2vw, 28px);
  background: rgba(255, 255, 255, .8);
  backdrop-filter: blur(26px) saturate(1.3);
  border: 1px solid rgba(255, 255, 255, .85);
  box-shadow: 0 40px 90px -60px rgba(50, 28, 120, .55);
}

.ov-hero__tagline {
  margin: 0;
  padding-left: clamp(16px, 2.4vw, 34px);
  border-left: 1px solid var(--ov-rule);
  font-family: var(--ov-font-display);
  font-size: clamp(19px, 1.7vw, 26px);
  line-height: 1.18;
  color: var(--ov-ink);
}
.ov-hero__tagline-a { display: block; }
.ov-mark--script {
  display: inline-block;
  font-style: italic;
  color: var(--ov-violet);
}

.ov-hero__promise {
  margin: 0;
  padding-left: clamp(16px, 2.4vw, 34px);
  border-left: 1px solid var(--ov-rule);
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ov-body-soft);
}

.ov-hero__stats {
  display: flex;
  align-items: center;
  gap: clamp(18px, 2.6vw, 40px);
}
.ov-hero__stats .ov-stat + .ov-stat {
  padding-left: clamp(18px, 2.6vw, 40px);
  border-left: 1px solid var(--ov-rule);
}

/* The stat band carries icons here too. It read as a bare row of numbers
   against a hero that is otherwise all shape and colour — the badges give the
   figures the same weight as the cards above them. */
.ov-hero__stats .ov-stat {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 0;
  border: 0;
}
.ov-hero__stats .ov-stat__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 50%;
}
.ov-hero__stats .ov-stat__badge .ov-icon { width: 24px; height: 24px; }
.ov-hero__stats .ov-stat--violet .ov-stat__badge { color: var(--ov-violet); background: rgba(237, 233, 254, .85); }
.ov-hero__stats .ov-stat--coral  .ov-stat__badge { color: #f2624f;         background: rgba(255, 228, 223, .85); }
.ov-hero__stats .ov-stat__text { display: grid; gap: 6px; max-width: 168px; }

.ov-about__stats .ov-stat {
  display: block;
  padding: 0;
  border: 0;
}
.ov-stat__num {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 40px;
  line-height: 1;
  letter-spacing: -.045em;
  color: var(--ov-ink);
}
.ov-stat--accent .ov-stat__num { color: var(--ov-violet); }
.ov-stat__label {
  margin: 7px 0 0;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ov-muted);
}

.ov-hero__tagline {
  margin: 0 0 6px;
  font-family: var(--ov-font-wordmark);
  font-weight: 300;
  font-size: 22px;
  letter-spacing: .06em;
  color: rgba(27, 16, 53, .5);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   8.3 Section furniture shared by the marketing sections
   -------------------------------------------------------------------------- */
.ov-front .ov-h2,
.ov-pagehead .ov-h2 {
  font-size: clamp(30px, 4.3vw, 58px);
  line-height: 1;
  letter-spacing: -.045em;
  color: var(--ov-ink);
  text-wrap: balance;
}
.ov-partners .ov-h2,
.ov-about .ov-h2 { font-size: clamp(28px, 3.6vw, 48px); line-height: 1.02; }
/* These modifiers have to outrank `.ov-front .ov-h2` and `.ov-front
   .ov-eyebrow` above, which are two-class selectors. A single modifier class
   loses to them, which is how the dark band ended up with a dark-ink heading
   and a violet eyebrow where the design has coral. */
.ov-h2--xl,
.ov-front .ov-h2--xl { font-size: clamp(34px, 5.6vw, 76px); line-height: .96; }
.ov-h2--light,
.ov-front .ov-h2--light { color: #fff; }

.ov-front .ov-eyebrow,
.ov-pagehead .ov-eyebrow {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .2em;
  color: var(--ov-violet-soft);
}
.ov-eyebrow--blue,
.ov-front .ov-eyebrow--blue  { color: var(--ov-blue); }
.ov-eyebrow--coral,
.ov-front .ov-eyebrow--coral { color: var(--ov-coral); }
.ov-front .ov-eyebrow--coral::after { background: linear-gradient(90deg, var(--ov-coral), transparent); }
.ov-front .ov-eyebrow--blue::after  { background: linear-gradient(90deg, var(--ov-blue), transparent); }

.ov-front .ov-head .ov-h2 { margin-top: 16px; }
.ov-front .ov-lede { margin-top: 22px; font-size: 16.5px; line-height: 1.65; }
.ov-lede--centred { margin-inline: auto; max-width: 480px; text-align: center; }

/* Buttons, in the four weights the design uses. */
.ov-btn--primary {
  padding: 17px 32px;
  border-radius: 16px;
  font-size: 15px;
  font-weight: 700;
  color: #fff;
  background: var(--ov-violet);
  border: 0;
  box-shadow: 0 24px 48px -20px rgba(91, 49, 214, .7);
}
.ov-btn--ink {
  padding: 15px 26px;
  border-radius: 16px;
  font-size: 14px;
  font-weight: 700;
  color: #fff;
  background: var(--ov-ink);
  border: 0;
}
.ov-btn--glass {
  padding: 15px 26px;
  border-radius: 16px;
  font-size: 14px;
  font-weight: 700;
  color: var(--ov-ink);
  background: rgba(255, 255, 255, .6);
  backdrop-filter: blur(18px);
  border: 1px solid rgba(91, 49, 214, .16);
}
.ov-btn--outline {
  padding: 14px 24px;
  border-radius: 14px;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  background: none;
  border: 1px solid rgba(255, 255, 255, .25);
}
.ov-btn--outline:hover { background: rgba(255, 255, 255, .1); color: #fff; }
.ov-btn--sm { padding: 14px 24px; border-radius: 14px; font-size: 13px; }
.ov-header__cta { background: rgba(27, 16, 53, .92); box-shadow: none; }
.ov-header__cta:hover { background: var(--ov-ink); }

/* The wave that separates two bands of colour.

   Each box is painted in the colour of the section BELOW it, and the path
   inside is filled with the colour of the section ABOVE — the shape is the
   upper section overhanging, not the lower one rising. Getting that backwards
   inverts every wave on the page. */
.ov-curve { position: relative; overflow: hidden; line-height: 0; }
.ov-curve svg { width: 100%; height: 100%; display: block; }

/* Pulled back over the hero, and transparent, so the photograph runs on
   behind it and the section ends on the wave instead of on a straight cut.
   The hero carries the matching padding so nothing of its own is covered. */
.ov-curve--hero {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 100px;
  background: transparent;
  z-index: 3;
}
.ov-curve--services { height: 120px; background: var(--ov-bg); }
.ov-curve--travel   { height: 110px; background: var(--ov-surface); }
.ov-curve--partners { height:  96px; background: var(--ov-bg); }
.ov-curve--about    { height: 130px; background: var(--ov-surface); }
.ov-curve--footer   { height:  96px; background: var(--ov-ink); }

.ov-curve--travel   .ov-curve__fill,
.ov-curve--about    .ov-curve__fill { fill: var(--ov-bg); }
.ov-curve--hero     .ov-curve__fill,
.ov-curve--services .ov-curve__fill,
.ov-curve--partners .ov-curve__fill,
.ov-curve--footer   .ov-curve__fill { fill: var(--ov-surface); }

/* The one over the closing call to action is the one the eye follows into the
   form, so it is drawn rather than simply present. */
.ov-curve__line--drawn {
  stroke-dasharray: 1800;
  --ov-dash: 1800;
  stroke-dashoffset: 1800;
}

/* Position comes inline from the helper; only the look lives here. */
.ov-curve__dot {
  position: absolute;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--ov-coral);
  box-shadow: 0 0 0 6px rgba(255, 122, 107, .16);
  animation: ovPulse 3s ease-in-out infinite;
}
.ov-curve__dot--lg {
  width: 11px; height: 11px;
  box-shadow: 0 0 0 7px rgba(255, 122, 107, .14);
  animation-duration: 3.4s;
}

/* Section auras. Each one is a single drifting disc placed where the design
   put it, rather than a generic glow applied everywhere. */
.ov-aura--services       { right: -140px; top: 120px;   width: 520px; height: 520px; background: radial-gradient(circle, rgba(139,92,246,.15), transparent 68%); animation: ovDrift 16s ease-in-out infinite; }
.ov-aura--process        { left: -160px;  bottom: -120px; width: 520px; height: 520px; background: radial-gradient(circle, rgba(59,130,246,.14), transparent 68%); animation: ovDrift2 19s ease-in-out infinite; }
.ov-aura--work           { right: -10%;   top: -100px;  width: 600px; height: 520px; background: radial-gradient(circle, rgba(139,92,246,.3), transparent 66%);  animation: ovDrift 17s ease-in-out infinite; }
.ov-aura--travel-blue    { left: 0;       top: 160px;   width: 660px; height: 600px; background: radial-gradient(circle, rgba(59,130,246,.16), transparent 68%); animation: ovDrift 16s ease-in-out infinite; }
.ov-aura--travel-coral   { right: 140px;  bottom: 150px; width: 620px; height: 560px; background: radial-gradient(circle, rgba(255,122,107,.15), transparent 68%); animation: ovDrift2 20s ease-in-out infinite; }
.ov-aura--contact-violet { left: 22%;     top: 60px;    width: 720px; height: 620px; background: radial-gradient(circle, rgba(139,92,246,.19), transparent 68%);  animation: ovDrift 15s ease-in-out infinite; }
.ov-aura--contact-coral  { right: 14%;    bottom: 170px; width: 680px; height: 580px; background: radial-gradient(circle, rgba(255,122,107,.16), transparent 68%); animation: ovDrift2 18s ease-in-out infinite; }

/* --------------------------------------------------------------------------
   8.4 Discipline marquee
   -------------------------------------------------------------------------- */
.ov-marquee { position: relative; background: var(--ov-surface); }

/* The rail dissolves at both ends instead of being chopped.

   Cut square, the first and last pills are sliced through the middle of a
   word, which reads as a rendering fault rather than as a rail that carries
   on past the screen. The fade says the same thing the cut was trying to. */
.ov-marquee__viewport {
  padding: 24px 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}

.ov-marquee__track {
  display: flex;
  width: max-content;
  /* Its own layer: the loop is a transform and nothing else, so the
     compositor can carry it without the main thread. */
  will-change: transform;
  animation: ovMarquee 30s linear infinite;
}

/* Stops when you point at it, so a term can actually be read. */
.ov-marquee:hover .ov-marquee__track,
.ov-marquee:focus-within .ov-marquee__track { animation-play-state: paused; }

.ov-marquee__run { display: flex; gap: 16px; padding-right: 16px; align-items: center; }

.ov-tag {
  padding: 11px 20px;
  border-radius: var(--ov-r-pill);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  color: var(--ov-ink);
  background: var(--ov-bg);
  border: 1px solid rgba(91, 49, 214, .14);
}
/* The two filled pills carry a shadow in their own colour. Flat, they sat on
   the rail as blocks of paint; lifted, they belong to the same surface as
   every other card on the page. */
.ov-tag--violet {
  background: var(--ov-violet);
  border-color: transparent;
  color: #fff;
  box-shadow: 0 10px 22px -12px rgba(91, 49, 214, .7);
}
.ov-tag--coral {
  background: var(--ov-coral);
  border-color: transparent;
  color: #fff;
  box-shadow: 0 10px 22px -12px rgba(255, 122, 107, .75);
}

/* --------------------------------------------------------------------------
   8.5 Services — bento grid
   -------------------------------------------------------------------------- */
.ov-services {
  position: relative;
  background: var(--ov-surface);
  padding: clamp(68px, 7.4vw, 104px) 0 clamp(78px, 8.4vw, 120px);
  overflow: hidden;
}
.ov-services .ov-wrap { position: relative; z-index: 1; }

/* The four-column grid this section used to be lives in section 22 now, at
   twelve columns and with rows that size to their content. Left here as well,
   its fixed 150px rows kept winning over the new ones and the cards overlapped
   each other by the sixty pixels they overflowed. */

.ov-bento__flag {
  position: relative;
  grid-column: span 2;
  grid-row: span 2;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 34px;
  border-radius: 30px;
  overflow: hidden;
  color: #fff;
  background: linear-gradient(155deg, var(--ov-violet), var(--ov-blue));
}
.ov-bento__flag .ov-kicker { position: relative; color: rgba(255, 255, 255, .75); font-size: 11.5px; letter-spacing: .18em; }
.ov-bento__orb {
  position: absolute;
  right: -60px; bottom: -70px;
  width: 260px; height: 260px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .14);
  animation: ovDrift 13s ease-in-out infinite;
}
.ov-bento__flagbody { position: relative; }
.ov-bento__title {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(30px, 3.4vw, 46px);
  line-height: .98;
  letter-spacing: -.045em;
}
.ov-bento__desc {
  margin: 14px 0 0;
  max-width: 320px;
  font-size: 14.5px;
  line-height: 1.55;
  color: rgba(255, 255, 255, .78);
}

.ov-tile {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 26px;
  border-radius: 30px;
  transition: transform .4s var(--ov-ease), box-shadow .4s var(--ov-ease);
}
.ov-tile:hover { transform: translateY(-4px); box-shadow: var(--ov-sh-md); }
.ov-tile--wide { grid-column: span 2; padding: 30px; }
.ov-tile--row  { flex-direction: row; align-items: center; justify-content: space-between; gap: 18px; }
.ov-tile--foot { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: 18px; }

.ov-tile--pale   { background: var(--ov-bg); border: 1px solid rgba(91, 49, 214, .12); color: var(--ov-ink); }
.ov-tile--lilac  { background: #ede4ff; color: var(--ov-ink); }
.ov-tile--coral  { background: var(--ov-coral); color: #fff; }
.ov-tile--violet { background: var(--ov-violet-soft); color: #fff; }
.ov-tile--ink    { background: var(--ov-ink); color: #fff; }

.ov-tile__n {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .16em;
  color: rgba(27, 16, 53, .45);
}
.ov-tile--coral .ov-tile__n,
.ov-tile--violet .ov-tile__n,
.ov-tile--ink .ov-tile__n { color: rgba(255, 255, 255, .75); }

.ov-tile__title {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 22px;
  line-height: 1.04;
  letter-spacing: -.035em;
}
.ov-tile__title--sm { font-size: 20px; margin-top: 4px; }
.ov-tile__title--md { font-size: clamp(22px, 2.2vw, 28px); line-height: 1.02; letter-spacing: -.04em; }
.ov-tile__title--lg { font-size: clamp(22px, 2.4vw, 30px); letter-spacing: -.04em; }
.ov-tile__sub { margin: 8px 0 0; font-size: 13.5px; line-height: 1.4; color: var(--ov-body-soft); }
.ov-tile__figure {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 26px;
  line-height: 1;
  letter-spacing: -.04em;
  color: var(--ov-violet);
}
.ov-tile__mark {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 40px;
  line-height: 1;
  color: rgba(91, 49, 214, .22);
}

/* Three bars, tallest in coral: the card is about creative output, so it gets
   the only chart on the page. */
.ov-bars { display: flex; align-items: flex-end; gap: 6px; height: 56px; flex: none; }
.ov-bars__b { width: 12px; border-radius: 4px; }
.ov-bars__b--1 { height: 40%;  background: rgba(255, 255, 255, .28); }
.ov-bars__b--2 { height: 68%;  background: var(--ov-violet-soft); }
.ov-bars__b--3 { height: 100%; background: var(--ov-coral); }

.ov-bento__band {
  grid-column: span 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding: 32px 38px;
  border-radius: 30px;
  background: var(--ov-bg);
  border: 1px solid rgba(91, 49, 214, .12);
}
.ov-bento__bandtitle {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(20px, 2.2vw, 28px);
  line-height: 1;
  letter-spacing: -.04em;
  color: var(--ov-ink);
}

/* --------------------------------------------------------------------------
   8.6 Process — six cards climbing a line
   -------------------------------------------------------------------------- */
.ov-process {
  position: relative;
  background: var(--ov-bg);
  padding: clamp(64px, 6.6vw, 92px) 0 clamp(80px, 8.6vw, 124px);
  overflow: hidden;
}
.ov-process .ov-wrap { position: relative; z-index: 1; }
.ov-process .ov-head { margin-bottom: clamp(28px, 3.4vw, 44px); }

/* Height comes from the tallest column now — the lift that makes the
   staircase is a margin, and a fixed 460px box cut the first card off and
   dropped it through the band below. See section 23. */
.ov-arc { position: relative; }
.ov-arc__line { position: absolute; inset: 0; pointer-events: none; }
.ov-arc__line svg { width: 100%; height: 100%; overflow: visible; display: block; }
/* The animation moved to section 23, where it waits for the section to be on
   screen. Left here it kept running at page load and finishing unseen — and
   because it fills both ways, the later rule could not undo it. */
.ov-arc__path { --ov-dash: 1600; }

/* Aligned to the bottom edge, so each card's own offset is the whole climb. */
.ov-arc__steps {
  position: relative;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 16px;
  height: 100%;
  align-items: end;
  margin: 0;
  padding: 0;
  list-style: none;
}
.ov-step {
  display: block;
  padding: 22px;
  border-radius: 26px;
}
.ov-step__n { display: block; font-size: 11px; font-weight: 600; letter-spacing: .16em; color: rgba(27, 16, 53, .4); }
.ov-step__title {
  display: block;
  margin-top: 8px;
  font-family: var(--ov-font-display);
  font-weight: 700;
  font-size: 20px;
  line-height: 1;
  letter-spacing: -.03em;
}
.ov-step__figure {
  display: block;
  margin-top: 10px;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 30px;
  line-height: 1;
}

.ov-step--1 { margin-bottom: 8px;   background: rgba(255, 255, 255, .72); backdrop-filter: blur(20px); border: 1px solid var(--ov-glass-brd); }
.ov-step--2 { margin-bottom: 56px;  background: #ede4ff; }
.ov-step--3 { margin-bottom: 118px; background: var(--ov-violet-soft); color: #fff; }
.ov-step--4 { margin-bottom: 178px; background: var(--ov-violet);      color: #fff; }
.ov-step--5 { margin-bottom: 236px; background: var(--ov-blue);        color: #fff; }
.ov-step--6 {
  margin-bottom: 296px;
  padding: 22px;
  border-radius: 28px;
  color: #fff;
  background: linear-gradient(160deg, var(--ov-coral), var(--ov-violet));
  box-shadow: 0 30px 60px -30px rgba(255, 122, 107, .7);
}
.ov-step--3 .ov-step__n,
.ov-step--4 .ov-step__n,
.ov-step--5 .ov-step__n,
.ov-step--6 .ov-step__n { color: rgba(255, 255, 255, .72); }
.ov-step--3 .ov-step__title,
.ov-step--4 .ov-step__title,
.ov-step--5 .ov-step__title { font-size: 22px; }
.ov-step--6 .ov-step__title { font-size: 24px; font-weight: 800; }

/* The descriptions sit outside the cards: inside, they would force every card
   to the height of the wordiest one and flatten the staircase. */
/* The descriptions moved inside the cards, where the design puts them. */

/* --------------------------------------------------------------------------
   8.7 Selected results — the dark band
   -------------------------------------------------------------------------- */
.ov-work {
  position: relative;
  padding: clamp(92px, 10.6vw, 150px) 0;
  color: #fff;
  background: var(--ov-ink);
  border-radius: 100% 100% 100% 100% / 90px 90px 90px 90px;
  overflow: hidden;
}
.ov-work .ov-wrap { position: relative; z-index: 1; }

.ov-cases { display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: 16px; }
.ov-case {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 288px;
  padding: 34px;
  border-radius: 30px;
  overflow: hidden;
  background: rgba(255, 255, 255, .07);
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, .14);
}
.ov-case--feature {
  background: linear-gradient(155deg, var(--ov-violet), var(--ov-violet-soft));
  border-color: transparent;
  backdrop-filter: none;
}
.ov-case__sweep {
  position: absolute;
  inset: 0;
  width: 180px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .1), transparent);
  animation: ovSweep 7s linear infinite;
  pointer-events: none;
}
@keyframes ovSweep {
  from { transform: translate3d(-200px, 0, 0); }
  to   { transform: translate3d(760px, 0, 0); }
}
.ov-case__top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.ov-case__sector { font-size: 11px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: rgba(255, 255, 255, .5); }
.ov-case--feature .ov-case__sector { color: rgba(255, 255, 255, .65); }
.ov-case__meta { font-size: 12px; font-weight: 700; color: var(--ov-coral); }
.ov-case__figure {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(42px, 4.6vw, 66px);
  line-height: 1;
  letter-spacing: -.05em;
}
.ov-case--feature .ov-case__figure { font-size: clamp(38px, 3.9vw, 56px); }
.ov-case__figure--accent { color: var(--ov-coral); }
.ov-case__desc { margin: 12px 0 0; max-width: 320px; font-size: 14.5px; line-height: 1.55; color: rgba(255, 255, 255, .62); }
.ov-case--feature .ov-case__desc { color: rgba(255, 255, 255, .78); }

.ov-quote {
  margin: clamp(30px, 4vw, 52px) 0 0;
  padding: clamp(30px, 3.8vw, 50px);
  border-radius: 34px;
  background: rgba(255, 255, 255, .06);
  backdrop-filter: blur(22px);
  border: 1px solid rgba(255, 255, 255, .14);
}
.ov-quote__text {
  margin: 0;
  max-width: 900px;
  font-family: var(--ov-font-display);
  font-weight: 400;
  font-size: clamp(19px, 2.3vw, 30px);
  line-height: 1.35;
  letter-spacing: -.02em;
  color: #fff;
  text-wrap: pretty;
  quotes: "\201C" "\201D";
}
.ov-quote__text::before { content: open-quote; }
.ov-quote__text::after  { content: close-quote; }
.ov-quote__by { display: flex; align-items: center; gap: 12px; margin-top: 22px; }
.ov-quote__avatar {
  width: 38px; height: 38px;
  flex: none;
  border-radius: 50%;
  background: linear-gradient(140deg, var(--ov-violet-soft), var(--ov-blue));
}
.ov-quote__who { display: flex; flex-direction: column; gap: 3px; }
.ov-quote__name { font-size: 13.5px; font-weight: 700; color: #fff; }
.ov-quote__role { font-size: 12.5px; color: rgba(255, 255, 255, .5); }

/* --------------------------------------------------------------------------
   8.8 Travel teaser
   -------------------------------------------------------------------------- */
.ov-travel {
  position: relative;
  background: var(--ov-bg);
  /* Tucked under the dark band's curved foot, which is the join here — the
     waves elsewhere on the page do the same job between two flat sections. */
  margin-top: -60px;
  padding: calc(60px + clamp(68px, 7.8vw, 112px)) 0 clamp(80px, 8.6vw, 124px);
  overflow: hidden;
}
.ov-travel__wrap {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
}
.ov-travel__cta { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 32px; }

.ov-travel__kinds {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.ov-kind {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 158px;
  padding: 26px;
  border-radius: 30px;
}
.ov-kind--lead {
  grid-column: span 2;
  flex-direction: row;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  min-height: 0;
  padding: 32px;
  background: rgba(255, 255, 255, .62);
  backdrop-filter: blur(26px) saturate(1.3);
  border: 1px solid var(--ov-glass-brd);
  box-shadow: 0 30px 70px -50px rgba(91, 49, 214, .7);
}
.ov-kind--blue  { background: var(--ov-blue); color: #fff; }
.ov-kind--lilac { background: #ede4ff; color: var(--ov-ink); }

.ov-kind__n { font-size: 11px; font-weight: 600; letter-spacing: .16em; color: rgba(27, 16, 53, .45); }
.ov-kind--blue .ov-kind__n { color: rgba(255, 255, 255, .75); }
.ov-kind__title {
  display: block;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 22px;
  line-height: 1.04;
  letter-spacing: -.035em;
}
.ov-kind--lead .ov-kind__title { font-size: clamp(22px, 2.4vw, 30px); letter-spacing: -.04em; }
.ov-kind__note { display: block; margin-top: 8px; font-size: 13.5px; line-height: 1.45; color: var(--ov-body-soft); }
.ov-kind__mark {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 40px;
  line-height: 1;
  color: rgba(59, 130, 246, .25);
}

/* --------------------------------------------------------------------------
   8.9 Partnerships
   -------------------------------------------------------------------------- */
.ov-partners { background: var(--ov-surface); padding: clamp(64px, 6.4vw, 88px) 0 clamp(78px, 8vw, 112px); }
.ov-partners__wrap {
  display: grid;
  grid-template-columns: 1fr 1.25fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}
.ov-partners__copy .ov-lede { max-width: 400px; }
.ov-tiers { display: grid; gap: 14px; }

.ov-tier {
  display: grid;
  grid-template-columns: 80px 1fr 200px;
  gap: 20px;
  align-items: center;
  padding: 30px;
  border-radius: 24px;
  background: var(--ov-bg);
  border: 1px solid rgba(91, 49, 214, .12);
  transition: transform .4s var(--ov-ease);
}
.ov-tier:hover { transform: translateX(4px); }
.ov-tier--feature {
  padding: 32px;
  color: #fff;
  background: var(--ov-violet);
  border-color: transparent;
  box-shadow: 0 30px 60px -40px rgba(91, 49, 214, .9);
}
.ov-tier__kicker { margin: 0; font-size: 11.5px; font-weight: 600; letter-spacing: .16em; color: var(--ov-violet-soft); }
.ov-tier--feature .ov-tier__kicker { color: rgba(255, 255, 255, .7); }
.ov-tier__title {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 26px;
  line-height: 1.04;
  letter-spacing: -.035em;
}
.ov-tier--feature .ov-tier__title { font-size: 30px; }
.ov-tier__desc { margin: 7px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--ov-body-soft); }
.ov-tier--feature .ov-tier__desc { color: rgba(255, 255, 255, .78); }
.ov-tier__meta { justify-self: end; display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--ov-violet); white-space: nowrap; }
.ov-tier--feature .ov-tier__meta { color: #fff; }

/* --------------------------------------------------------------------------
   8.10 About
   -------------------------------------------------------------------------- */
.ov-about { background: var(--ov-bg); padding: clamp(60px, 5.8vw, 80px) 0 clamp(78px, 8vw, 112px); }
.ov-about__wrap {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
}
.ov-about__copy .ov-lede { max-width: 440px; }
.ov-about__stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }

.ov-figure {
  padding: 30px;
  border-radius: 26px;
  background: rgba(255, 255, 255, .7);
  backdrop-filter: blur(20px);
  border: 1px solid var(--ov-glass-brd);
}
.ov-figure--ink { background: var(--ov-ink); border-color: transparent; color: #fff; }
.ov-figure__num {
  margin: 0;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 38px;
  line-height: 1;
  letter-spacing: -.04em;
  color: var(--ov-violet);
}
.ov-figure--ink .ov-figure__num { color: #fff; }
.ov-figure__label {
  margin: 8px 0 0;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ov-muted);
}
.ov-figure--ink .ov-figure__label { color: rgba(255, 255, 255, .5); }

/* --------------------------------------------------------------------------
   8.11 Closing call to action
   -------------------------------------------------------------------------- */
.ov-contact {
  position: relative;
  background: var(--ov-surface);
  padding: clamp(68px, 7.2vw, 104px) clamp(24px, 4vw, 104px) clamp(78px, 8.4vw, 120px);
  overflow: hidden;
}
.ov-contact__wrap { position: relative; z-index: 1; max-width: 900px; margin-inline: auto; }
.ov-contact__copy { text-align: center; }
.ov-contact__cta { display: flex; justify-content: center; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 34px; }

.ov-contact__panel {
  max-width: 560px;
  margin: clamp(34px, 4vw, 52px) auto 0;
  padding: clamp(26px, 3vw, 36px);
  border-radius: var(--ov-r-2xl);
  background: rgba(255, 255, 255, .66);
  backdrop-filter: blur(26px) saturate(1.3);
  border: 1px solid rgba(91, 49, 214, .12);
  box-shadow: 0 40px 80px -50px rgba(91, 49, 214, .7);
}
.ov-contact__direct {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 20px 0 0;
  font-size: 13.5px;
  font-weight: 600;
}
.ov-contact__direct a { color: var(--ov-violet); }
.ov-contact__direct span { color: var(--ov-muted); }

/* --------------------------------------------------------------------------
   8.12 Dark footer
   -------------------------------------------------------------------------- */
.ov-footer {
  background: var(--ov-ink);
  color: #fff;
  padding: 52px clamp(24px, 4vw, 104px);
}
.ov-footer__grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 1fr;
  gap: 40px;
  max-width: var(--ov-max-wide);
  margin-inline: auto;
  padding-bottom: 40px;
  border-bottom: 1px solid rgba(255, 255, 255, .12);
}
.ov-footer__about { margin: 18px 0 0; max-width: 280px; font-size: 14px; line-height: 1.6; color: rgba(255, 255, 255, .5); }
.ov-footer__col { display: flex; flex-direction: column; gap: 10px; }
.ov-footer__title {
  margin: 0 0 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .35);
}
.ov-footer__col a,
.ov-footer__col li a { font-size: 14px; color: rgba(255, 255, 255, .8); }
.ov-footer__col a:hover { color: #fff; }
.ov-footer__col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.ov-footer__muted { font-size: 13px; line-height: 1.5; color: rgba(255, 255, 255, .4); }

.ov-footer .ov-brand__name { color: #fff; }
.ov-footer .ov-brand__sub  { color: rgba(139, 92, 246, .95); }
/* The chip is a light glass pill in the base rule, which the dark footer has
   to undo: a near-white icon on a near-white background is what it used to
   render. Here it turns into the inverse — a sliver of white over the ink,
   with the icon bright enough to read against it. */
.ov-footer .ov-social a {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .22);
  color: rgba(255, 255, 255, .8);
  box-shadow: none;
  transition: transform .25s var(--ov-ease), background .2s, color .2s;
}
.ov-footer .ov-social a:hover {
  background: rgba(255, 255, 255, .22);
  color: #fff;
}

.ov-footer__bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  max-width: var(--ov-max-wide);
  margin: 22px auto 0;
  padding-top: 18px;
  /* The rule above the copyright was an ocean blue at 12% — a colour from the
     travel site, and on a violet-black ground barely a rule at all. Light
     violet reads on the dark and belongs to this palette. */
  border-top: 1px solid rgba(196, 181, 253, .18);
  font-size: 12.5px;
  color: rgba(255, 255, 255, .45);
}
.ov-footer__bottom p { margin: 0; }
.ov-footer__legal { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ov-footer__legal a { color: rgba(255, 255, 255, .55); }
.ov-footer__legal a:hover { color: #fff; }
.ov-footer__disclaimer {
  max-width: var(--ov-max-wide);
  margin: 14px auto 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: rgba(255, 255, 255, .3);
}

/* --------------------------------------------------------------------------
   8.13 Travel page header
   -------------------------------------------------------------------------- */
.ov-pagehead {
  position: relative;
  background: var(--ov-bg);
  padding: clamp(60px, 6.6vw, 100px) 0 clamp(52px, 5.8vw, 78px);
  overflow: hidden;
}
.ov-pagehead__aura { position: absolute; inset: -220px; pointer-events: none; opacity: .7; }
.ov-pagehead__wrap { position: relative; z-index: 1; }
.ov-pagehead .ov-h1--page { margin: 22px 0 0; max-width: 18ch; }
.ov-pagehead .ov-lede { margin-top: 22px; }
.ov-pagehead__back { margin: 30px 0 0; font-size: 13.5px; font-weight: 600; }
.ov-pagehead__back a { display: inline-flex; align-items: center; gap: 8px; color: var(--ov-violet); }

/* The base rule sets an explicit colour on every heading, which stops a card
   title inheriting from the card it sits on: on the coral, violet and ink
   cards the titles were rendering in dark ink on a dark ground. These titles
   take their colour from the card, always. */
.ov-bento__title,
.ov-tile__title,
.ov-tier__title,
.ov-quote__text { color: inherit; }

/* --------------------------------------------------------------------------
   8.14 Marketing — responsive
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .ov-bento__flag { grid-column: span 2; grid-row: span 2; }
  .ov-bento__band { grid-column: span 4; }

  /* The staircase needs six columns to be a staircase. Below that the cards
     stack and the climb becomes noise, so the line goes and the offsets with
     it — the numbers still carry the order. */
  .ov-arc { height: auto; }
  .ov-arc__line { display: none; }
  .ov-arc__steps { grid-template-columns: repeat(3, 1fr); gap: 14px; align-items: stretch; }
  .ov-step[class*="ov-step--"] { margin-bottom: 0; }
  .ov-arc__notes { grid-template-columns: repeat(3, 1fr); }
  .ov-arc__note { grid-column: span 1; }

  .ov-cases { grid-template-columns: 1fr 1fr; }
  .ov-case--feature { grid-column: span 2; }
}

@media (max-width: 900px) {
  .ov-hero__grid { grid-template-columns: 1fr; }
  .ov-hero__copy { padding-top: 0; }

  /* One column: the cards cannot stay pinned to the curve, because at this
     width they would cover it and each other. The chart becomes a band and
     they fall into a plain grid underneath, still in the order they were
     read on the curve. */
  /* The photograph drops to a band across the foot of the section. Kept where
     it is on a phone it sits directly behind the headline — the copy column is
     the whole width here — and a boardroom dashboard behind body text is not
     something a reader can look past. */
  .ov-hero__photo {
    inset: auto 0 0 0;
    height: 56%;
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 36%);
            mask-image: linear-gradient(180deg, transparent 0, #000 36%);
  }
  .ov-hero__photo img { transform: none; object-position: 58% 50%; }
  .ov-hero__veil { display: none; }

  .ov-hero__chart { aspect-ratio: auto; margin-top: 30px; }
  /* The drawing joins the flow instead of being a box the cards sit inside —
     left absolute, it kept its height and they piled on top of it. */
  .ov-chart {
    position: static;
    aspect-ratio: 16 / 9;
    height: auto;
  }
  /* The dashed leaders point at cards that are no longer beside the curve. */
  .ov-chart__links { display: none; }

  .ov-hero__floats {
    position: static;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin-top: 16px;
  }
  .ov-hero__floats > * { position: static; }
  .ov-card--signal { grid-column: 1 / -1; width: 100%; }
  .ov-hero__floats .ov-chip--ink { justify-self: start; }
  .ov-note { width: auto; max-width: none; }

  /* The panel becomes a stack: three columns of this at phone width would be
     three unreadable slivers. */
  .ov-hero__panel { grid-template-columns: 1fr; gap: 18px; }
  .ov-hero__tagline,
  .ov-hero__promise { padding-left: 0; border-left: 0; }
  .ov-hero__stats { flex-wrap: wrap; }
  .ov-hero__tagline { white-space: normal; }

  .ov-services .ov-bento { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .ov-bento__band { grid-column: span 2; }

  .ov-cases { grid-template-columns: 1fr; }
  .ov-case--feature { grid-column: span 1; }

  .ov-travel__wrap,
  .ov-about__wrap,
  .ov-partners__wrap { grid-template-columns: 1fr; }

  /* 80px / 1fr / 200px only works while there is room for three columns. */
  .ov-tier { grid-template-columns: 1fr; gap: 10px; }
  .ov-tier__meta { justify-self: start; }

  .ov-head--row { align-items: flex-start; flex-direction: column; }
  .ov-head .ov-h2 { max-width: none; }

  .ov-work { border-radius: 100% 100% 100% 100% / 48px 48px 48px 48px; }

  .ov-footer__grid { grid-template-columns: 1fr 1fr; gap: 28px; }
}

@media (max-width: 640px) {
  /* At 390px the headline's longest line ("businesses forward.") ran to within
     a few pixels of the edge at the clamp's floor. */
  .ov-front .ov-h1,
  .ov-h1--page { font-size: clamp(30px, 8.4vw, 38px); }

  /* A full-width button made the sticky bar wrap to two rows and stand 145px
     tall — a sixth of the screen, permanently sticky. Brand, burger and button
     do not fit on one row at this width, so the button goes: the hero repeats
     it immediately below, and the menu behind the burger ends with Contact. */
  .ov-header__cta { display: none; }

  .ov-services .ov-bento { grid-template-columns: 1fr; }
  .ov-bento__flag,
  .ov-tile--wide,
  .ov-bento__band { grid-column: span 1; }
  .ov-tile { min-height: 132px; }
  .ov-tile--row, .ov-tile--foot { flex-direction: row; }

  .ov-arc__steps { grid-template-columns: repeat(2, 1fr); }
  .ov-arc__notes { grid-template-columns: 1fr; }

  .ov-travel__kinds { grid-template-columns: 1fr; }
  .ov-kind--lead { grid-column: span 1; flex-direction: column; }

  .ov-about__stats { grid-template-columns: 1fr; }

  .ov-hero__stats { gap: 22px; flex-wrap: wrap; }
  .ov-stat__num { font-size: 32px; }

  .ov-curve--hero { height: 60px; }
  .ov-curve__dot { top: 38px; }
  .ov-hero__tagline { font-size: 17px; }

  .ov-footer__grid { grid-template-columns: 1fr; }
  .ov-footer__bottom { flex-direction: column; align-items: flex-start; }
}

/* --------------------------------------------------------------------------
   8.13 Brand lockup
   The wordmark is set as text rather than shipped as a second PNG: it has to
   sit next to the mark at three sizes, and an image of letterforms goes soft
   at exactly the size the header uses it.
   -------------------------------------------------------------------------- */
.ov-brand__lockup {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: var(--ov-font-wordmark);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}
.ov-brand__name {
  font-size: clamp(15px, 1.3vw, 17px);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ov-ink);
}
.ov-brand__sub {
  font-size: clamp(8px, .72vw, 8.5px);
  letter-spacing: .42em;
  text-transform: uppercase;
  color: var(--ov-blue-soft);
}
.ov-brand__mark { width: clamp(46px, 3.6vw, 62px); filter: none; }

.ov-footer__brand { display: flex; align-items: center; gap: 11px; }
.ov-footer__brand .ov-brand__mark { width: 56px; }
.ov-footer .ov-brand__name { color: inherit; }

/* The header bar picks up the lavender ground rather than the ocean blue the
   travel build tinted it with. */
.ov-front .ov-header__shell,
.ov-pagehead ~ * .ov-header__shell { box-shadow: 0 18px 44px -30px rgba(91, 49, 214, .5); }
.ov-header__shell::before { background: rgba(247, 245, 255, .3); }
.ov-header.is-scrolled { filter: drop-shadow(0 12px 26px rgba(91, 49, 214, .18)); }
/* The base .ov-h1 breaks inside words so a long destination name cannot
   overflow the Travel page's header. At 90px on a marketing headline that
   reads as a typo, so the front page opts out. */
.ov-front .ov-h1,
.ov-h1--page { overflow-wrap: normal; }

/* ==========================================================================
   9. Ground
   The lavender was a single flat fill, which at this size banded on gradients
   and left the pale sections looking like empty paper. Three things fix that:
   a soft field instead of a flat colour, auras large enough to actually read
   as light rather than as blobs, and a grain fine enough to be felt and not
   seen — which is also what kills the banding.
   ========================================================================== */
:root {
  --ov-bg-deep: #f4f0ff;
  --ov-bg-warm: #fdf6f5;

  /* One tile of fractal noise, inlined. A texture this small costs nothing and
     never leaves the document — no request, no cache, no third party.
     The opacity is baked into the SVG rather than applied with mix-blend-mode:
     blending a full-section overlay makes the compositor treat the whole band
     as one blended layer, which is both more work than a texture is worth and
     what stopped it rendering in a screenshot at all. Two tiles, because black
     specks do nothing on an ink ground. */
  --ov-grain:       url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.06'/%3E%3C/svg%3E");
  --ov-grain-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.05'/%3E%3C/svg%3E");
}

/* Lavender sections: a field that drifts from cool to warm rather than one
   flat value, so the page has a direction of light. */
.ov-hero,
.ov-process,
.ov-travel,
.ov-about,
.ov-pagehead {
  /* Fixed attachment: the light is one field over the whole page rather than a
     copy of itself restarted at every section's top edge, which is what made
     the joins read as steps. */
  background:
    radial-gradient(120% 90% at 12% 0%,   rgba(139, 92, 246, .05),  transparent 62%),
    radial-gradient(100% 80% at 88% 18%,  rgba(59, 130, 246, .04),  transparent 60%),
    radial-gradient(90% 70% at 60% 100%,  rgba(255, 122, 107, .035), transparent 62%),
    var(--ov-bg);
  background-attachment: fixed, fixed, fixed, scroll;
}

/* White sections: not paper-white, but a page lit from the top. */
.ov-services,
.ov-partners,
.ov-contact,
.ov-marquee {
  background:
    radial-gradient(120% 90% at 20% 0%, rgba(139, 92, 246, .035), transparent 60%),
    radial-gradient(100% 80% at 85% 60%, rgba(59, 130, 246, .03), transparent 58%),
    var(--ov-surface);
  background-attachment: fixed, fixed, scroll;
}

/* The dark band gets the same treatment in reverse: a violet cast from the top
   right, so it is not a black rectangle dropped on the page. */
.ov-work {
  background:
    radial-gradient(80% 60% at 88% 0%,  rgba(139, 92, 246, .26), transparent 62%),
    radial-gradient(70% 60% at 8% 100%, rgba(59, 130, 246, .14), transparent 60%),
    var(--ov-ink);
}
.ov-footer {
  position: relative;
  /* Room for the wave, which now sits inside and is pinned to the top edge. */
  padding-top: 148px;
  background:
    radial-gradient(70% 120% at 12% 0%, rgba(139, 92, 246, .2), transparent 62%),
    var(--ov-ink);
}

.ov-curve--footer {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  background: transparent;
}

/* Grain. Sits above the fill and below the content, so nothing needs a
   stacking context of its own except the wrapper the content already has. */
.ov-hero::after,
.ov-services::after,
.ov-process::after,
.ov-travel::after,
.ov-partners::after,
.ov-about::after,
.ov-contact::after,
.ov-work::after,
.ov-pagehead::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: var(--ov-grain);
  background-size: 180px 180px;
  pointer-events: none;
}
.ov-work::after { background-image: var(--ov-grain-light); }

.ov-partners,
.ov-about { position: relative; }

/* Anything sitting on a grained section needs to be above the grain. */
.ov-hero__inner,
.ov-services .ov-wrap,
.ov-process .ov-wrap,
.ov-travel__wrap,
.ov-partners__wrap,
.ov-about__wrap,
.ov-contact__wrap,
.ov-work .ov-wrap,
.ov-pagehead__wrap { position: relative; z-index: 1; }

/* The auras were sized for a 1300px column. On a full-width page they have to
   grow with it or they read as three lonely dots. */
/* No blur.

   These are radial gradients that already fade to nothing well before their
   own edge, so a 10px blur on top of them changes almost nothing to look at —
   and it changes everything to draw. Seventeen of these, some half a million
   pixels each, all animating for ever: with a filter in the way the browser
   cannot composite the animation and re-renders the blur every frame. Measured
   on this page, that alone was the difference between 7fps and 52fps while
   scrolling.

   will-change puts each one on its own layer, so the drift is a transform the
   compositor moves rather than a repaint. */
.ov-aura { will-change: transform; }
.ov-aura--services     { right: -10%; top: 60px;    width: min(46vw, 760px); height: min(46vw, 760px); }
.ov-aura--process      { left: -12%;  bottom: -14%; width: min(48vw, 800px); height: min(48vw, 800px); }
.ov-aura--work         { right: -8%;  top: -140px;  width: min(52vw, 900px); height: min(40vw, 700px); }
.ov-aura--travel-blue  { left: -6%;   top: 100px;   width: min(50vw, 860px); height: min(46vw, 780px); }
.ov-aura--travel-coral { right: 4%;   bottom: 90px; width: min(46vw, 800px); height: min(42vw, 720px); }
.ov-aura--contact-violet { left: 14%;  top: 20px;    width: min(52vw, 920px); height: min(46vw, 800px); }
.ov-aura--contact-coral  { right: 6%;  bottom: 120px; width: min(48vw, 860px); height: min(42vw, 740px); }

/* Grain is texture, not motion, but the auras drift. Under reduced motion the
   whole thing should simply be a still, soft field. */
@media (prefers-reduced-motion: reduce) {
  .ov-aura { animation: none !important; }
}

/* --------------------------------------------------------------------------
   8.15 Navigation
   -------------------------------------------------------------------------- */

/* The shell was clipping its own dropdowns. overflow:hidden was there to round
   the glass bar's corners, but a submenu hangs below the bar by definition, so
   it was being cut off at the bar's edge and read as part of the bar rather
   than as a panel over the page. The bar paints its own rounded corners
   already; only the tint overlay needed telling. */
.ov-header__shell { overflow: visible; }
.ov-header__shell::before { border-radius: inherit; }

/* Clear of the bar, not tucked behind it: the bar is translucent, so an
   overlapping panel showed through the glass. */
.ov-nav .sub-menu { top: calc(100% + 30px); }
.ov-nav .sub-menu {
  background: rgba(255, 255, 255, .92);
  border-color: var(--ov-glass-brd-2);
  box-shadow: 0 30px 60px -28px rgba(91, 49, 214, .45);
  padding: 14px;
  gap: 2px;
}
.ov-nav .sub-menu a {
  padding: 9px 12px;
  border-radius: 10px;
  font-size: 13.5px;
  color: var(--ov-body-soft);
  transition: background .2s var(--ov-ease), color .2s var(--ov-ease);
}
.ov-nav .sub-menu a:hover,
.ov-nav .sub-menu a:focus-visible {
  color: var(--ov-violet);
  background: var(--ov-tint-violet);
}

/* The panel opens 14px below the bar. Without something spanning that gap the
   pointer leaves the item on its way down and the menu closes underneath it. */
.ov-nav > li.menu-item-has-children::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 32px;
  display: none;
}
.ov-nav > li.menu-item-has-children:hover::after,
.ov-nav > li.menu-item-has-children:focus-within::after { display: block; }

.ov-nav .menu-item-has-children > a::after {
  border-right-color: var(--ov-violet);
  border-bottom-color: var(--ov-violet);
  transition: transform .25s var(--ov-ease);
}
.ov-nav > li.menu-item-has-children:hover > a::after,
.ov-nav > li.menu-item-has-children:focus-within > a::after {
  transform: rotate(-135deg);
  margin-bottom: -2px;
}

.ov-nav > li > a { transition: color .2s var(--ov-ease); }
.ov-nav > li > a:hover { color: var(--ov-violet); }

/* On the phone the menu is a stacked list inside the bar, so none of the
   floating behaviour applies and the gap bridge would sit over the content. */
@media (max-width: 900px) {
  .ov-nav > li.menu-item-has-children::after { display: none !important; }
  .ov-nav .sub-menu { box-shadow: none; }
}


/* ==========================================================================
   10. Usability
   ========================================================================== */

/* Every anchor in the navigation landed its section under the sticky header:
   93px of glass sitting on top of the heading you just asked to see. */
:where(#top, #services, #process, #work, #travel, #partners, #about, #contact,
       #membership, #savings, #points, #tours, #getaways, #faq, #join) {
  scroll-margin-top: 112px;
}
@media (max-width: 860px) {
  :where(#top, #services, #process, #work, #travel, #partners, #about, #contact,
         #membership, #savings, #points, #tours, #getaways, #faq, #join) {
    scroll-margin-top: 100px;
  }
}

/* Which section you are in. Nothing in the bar said so before, so on a page
   this long the nav was a list of links rather than a position. */
.ov-nav > li > a.is-current {
  color: var(--ov-violet);
  font-weight: 700;
}
.ov-nav > li > a.is-current::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -6px;
  height: 2px;
  border-radius: 2px;
  background: var(--ov-violet);
}
.ov-nav > li { position: relative; }

/* Contrast. These are the design's own alpha values, and several of them land
   under 4.5:1 on the pale ground — the figure captions at 2.9, the brand line
   at 3.4, the footer's small print at 2.7. Raised to the least that clears AA,
   which is a change of tone nobody will notice and a difference to anyone
   reading at arm's length. */
.ov-stat__label,
.ov-figure__label,
.ov-tile__n,
.ov-kind__n,
.ov-step__n { color: rgba(27, 16, 53, .66); }

/* Lives on the dark band, so it goes the other way. */
.ov-case__sector { color: rgba(255, 255, 255, .62); }

.ov-formnote { color: rgba(27, 16, 53, .68); }
.ov-hero__tagline { color: rgba(27, 16, 53, .62); }

/* On ink, the same problem in reverse. */
.ov-footer__muted { color: rgba(255, 255, 255, .62); }
.ov-footer__disclaimer { color: rgba(255, 255, 255, .5); }
.ov-footer__bottom { color: rgba(255, 255, 255, .6); }
.ov-quote__role { color: rgba(255, 255, 255, .66); }
.ov-case__desc { color: rgba(255, 255, 255, .74); }

/* The cards that are already coloured keep their own light-on-dark labels. */
.ov-tile--coral .ov-tile__n,
.ov-tile--violet .ov-tile__n,
.ov-tile--ink .ov-tile__n,
.ov-kind--blue .ov-kind__n,
.ov-step--3 .ov-step__n,
.ov-step--4 .ov-step__n,
.ov-step--5 .ov-step__n,
.ov-step--6 .ov-step__n { color: rgba(255, 255, 255, .8); }
.ov-case--feature .ov-case__sector { color: rgba(255, 255, 255, .78); }

/* --------------------------------------------------------------------------
   The strip behind the header
   The header floats above the hero with page background showing around it, and
   that background was plain --ov-bg while the hero underneath carried the tint
   and the lattice. The result was a pale band across the top of every page
   ending in a hard horizontal edge under the bar: the background looked cut
   off, because it was.

   The page wrapper now carries the same ground as the hero. Both are anchored
   to the viewport, so they line up exactly and the edge disappears.
   -------------------------------------------------------------------------- */
.ov-page {
  position: relative;
  background:
    radial-gradient(130% 105% at 50% -12%, rgba(139, 92, 246, .19), transparent 68%),
    radial-gradient(90% 80% at 88% -4%,   rgba(59, 130, 246, .16),  transparent 62%),
    radial-gradient(80% 70% at 12% 0%,    rgba(196, 181, 253, .18), transparent 60%),
    var(--ov-bg);
  background-attachment: fixed, fixed, fixed, scroll;
}
.ov-page::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: var(--ov-lattice);
  background-size: var(--ov-lattice-size);
  background-attachment: fixed;
}
.ov-header { position: sticky; z-index: 60; }

@media (max-width: 860px), (hover: none) {
  .ov-page, .ov-page::before { background-attachment: scroll; }
}

/* ==========================================================================
   11. Ground, second pass
   The lavender had light and grain but no structure. A dot lattice gives it
   something to be: the measuring paper a growth line gets drawn on, which is
   what this whole page is about. It is set far below the text in contrast so
   it reads as paper rather than as pattern.
   ========================================================================== */
:root {
  --ov-lattice:       radial-gradient(circle at 1px 1px, rgba(91, 49, 214, .16) 1px, transparent 0);
  --ov-lattice-light: radial-gradient(circle at 1px 1px, rgba(255, 255, 255, .13) 1px, transparent 0);
  --ov-lattice-size:  34px 34px;
}

/* One grid over the whole page. Fixed attachment again, for the same reason:
   tiled per section, the dots restart at every top edge and every boundary
   becomes a visible line in the pattern. Fixed, the lattice is continuous
   through the sections AND through the waves between them. */
.ov-hero::before,
.ov-services::before,
.ov-process::before,
.ov-travel::before,
.ov-partners::before,
.ov-about::before,
.ov-contact::before,
.ov-work::before,
.ov-pagehead::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: var(--ov-lattice);
  background-size: var(--ov-lattice-size);
  background-attachment: fixed;
  /* A soft vignette rather than a hard stop: the pattern is strongest at the
     edges of the page and clears out where the text sits. */
  -webkit-mask-image: radial-gradient(140% 92% at 50% 50%, transparent 0%, #000 88%);
          mask-image: radial-gradient(140% 92% at 50% 50%, transparent 0%, #000 88%);
}
.ov-work::before { background-image: var(--ov-lattice-light); }

/* The hero keeps clear of the headline, which is the one place on the page
   where nothing should compete. */
.ov-hero::before {
  -webkit-mask-image: radial-gradient(95% 78% at 26% 44%, transparent 0%, #000 80%);
          mask-image: radial-gradient(95% 78% at 26% 44%, transparent 0%, #000 80%);
}

/* The waves carried no texture at all, so each one was a plain stripe cutting
   the pattern in two. They get the same fixed grid, which lines up with the
   sections either side of them precisely because it is fixed. */
.ov-curve::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: var(--ov-lattice);
  background-size: var(--ov-lattice-size);
  background-attachment: fixed;
  opacity: .8;
}
.ov-curve--footer::after { background-image: var(--ov-lattice-light); }

.ov-services, .ov-partners, .ov-contact { position: relative; }

/* Fixed backgrounds are repainted on every scroll frame and iOS drops them
   entirely, so on touch and on small screens the ground goes back to scrolling
   with the page. The joins matter less there: a phone shows one band at a
   time, so there is rarely a junction on screen to notice. */
@media (max-width: 860px), (hover: none) {
  .ov-hero, .ov-services, .ov-process, .ov-travel,
  .ov-partners, .ov-about, .ov-contact, .ov-pagehead { background-attachment: scroll; }
  .ov-hero::before, .ov-services::before, .ov-process::before, .ov-travel::before,
  .ov-partners::before, .ov-about::before, .ov-contact::before,
  .ov-work::before, .ov-pagehead::before,
  .ov-curve::after { background-attachment: scroll; }
}


/* ==========================================================================
   12. One ground
   Every pale band was painting its own background on top of the page's, and
   they were not the same paint: the page carried the tint and the lattice
   while the sections carried flat --ov-bg. Around the header, where the page
   shows through, that difference drew a line above the bar and another below
   it.

   So the pale bands stop painting. The ground is laid once, on the wrapper,
   and the sections let it through. There is no join to hide because there is
   no second surface.
   ========================================================================== */
:root {
  /* Kept at full strength on purpose. It was flattened once to chase a
     near-white mockup, and the drifting colour fields are the thing that makes
     this page look like itself — the hero has a photograph to carry it now,
     but every section below is still standing on this. */
  --ov-ground:
    radial-gradient(130% 105% at 50% -12%, rgba(139, 92, 246, .19), transparent 68%),
    radial-gradient(90% 80% at 88% -4%,   rgba(59, 130, 246, .16),  transparent 62%),
    radial-gradient(80% 70% at 12% 0%,    rgba(196, 181, 253, .18), transparent 60%);
}

.ov-page {
  background: var(--ov-ground), var(--ov-bg);
  background-attachment: fixed, fixed, fixed, scroll;
}

.ov-hero,
.ov-process,
.ov-about,
.ov-pagehead { background: transparent; }

.ov-hero::before,
.ov-process::before,
.ov-about::before,
.ov-pagehead::before { content: none; }

/* The travel teaser is the exception: it is pulled up over the dark band's
   curved foot, so it has to be opaque or the dark shape shows through it. It
   paints the same ground, and both are anchored to the viewport, so the two
   surfaces line up exactly and the seam is not there to see. */
.ov-travel {
  background: var(--ov-ground), var(--ov-bg);
  background-attachment: fixed, fixed, fixed, scroll;
}
.ov-travel::before {
  -webkit-mask-image: none;
          mask-image: none;
}

@media (max-width: 860px), (hover: none) {
  .ov-page,
  .ov-travel { background-attachment: scroll; }
}


/* The aura layer, lifted out of the hero.
   Clipped by the hero's overflow it started at the hero's top edge and drew a
   line under the header. It spans the top of the page now — tall enough to
   cover the first screen and fade out, and behind everything. */
.ov-page__aura {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: min(1400px, 160vh);
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.ov-page__aura .ov-aura { position: absolute; }

/* The aura goes behind by its own z-index, contained by isolation on the
   wrapper, rather than by pushing every sibling in front of it.
   `.ov-page > * { position: relative }` did that, and it also overrode the
   header's `position: sticky` — the bar stopped following the page, and
   because the current-section marker measures from the header's own bottom
   edge, the marker went with it. One line, two things broken, neither of them
   where the line was written. */
.ov-page { isolation: isolate; }
.ov-page__aura { z-index: -1; }

/* ==========================================================================
   13. Hero, second pass
   ========================================================================== */

/* The floating pieces were stacked into a tidy column here, because scattered
   across an empty box they read as debris rather than as a cluster. That was
   the right call at the time and is the wrong one now: the emptiness was the
   problem, and there is a curve behind them to be pinned to. Cards that name a
   point on a chart are not debris — they are labels. The stack is gone; the
   placement lives with the chart, in percentages of the area it is drawn in. */

/* The band of figures was landing across the fold on a 900px screen: numbers
   visible, captions cut. Pulled up so it either sits inside the first screen
   or is clearly below it, never halfway. */
.ov-hero__panel { margin-top: 30px; }
/* Room for the wave. The divider is pulled back a hundred pixels over this
   section so the photograph ends on the curve, and without the matching space
   the wave's white half cuts across the figures panel. */
.ov-hero { padding-bottom: clamp(148px, 4.4vw + 104px, 172px); }

/* The chip that left the hero, in its new home. */
.ov-about__reach { margin-top: 22px; }

@media (max-width: 900px) {
  .ov-hero__floats { align-items: stretch; }
  .ov-hero__floats .ov-chip--ink { margin-right: 0; }
  .ov-card--signal { width: 100%; }
}

/* ==========================================================================
   14. Motion
   The theme already carried a parallax layer, a wipe reveal and a pointer
   sheen from the travel build; the redesign left all three unwired. They are
   put back to work here rather than written again.
   ========================================================================== */

/* The light that runs the curve. It starts after the curve has drawn itself,
   then repeats slowly — often enough to catch the eye once, rare enough not
   to become a blinking thing in the corner of the screen. */
.ov-chart__spark {
  stroke-dasharray: 22 1500;
  stroke-dashoffset: 1522;
  opacity: .85;
  filter: blur(.4px);
  animation: ovSpark 4.6s cubic-bezier(.55, 0, .45, 1) 2.6s infinite;
}
@keyframes ovSpark {
  0%       { stroke-dashoffset: 1522; opacity: 0; }
  8%       { opacity: .9; }
  55%,100% { stroke-dashoffset: 0;    opacity: 0; }
}

/* The headline reveals by uncovering rather than by fading in. */
.ov-h1.ov-wipe { clip-path: inset(0 100% 0 0); transition: clip-path 1.1s cubic-bezier(.2, .7, .2, 1); }
.ov-h1.ov-wipe.is-visible { clip-path: inset(0 0 0 0); }

/* Depth. Two layers moving at different speeds against the scroll, which is
   what stops a hero this flat from feeling like a printed page. Small numbers
   on purpose: enough to feel, not enough to notice. */
.ov-hero__floats[data-parallax],
.ov-hero__line[data-parallax] { will-change: transform; }

/* The signal card answers the pointer. The sheen is the theme's own, the tilt
   is new: the card is the one object in the hero that reads as a thing rather
   than as type, so it is the one worth making touchable. */
.ov-card--signal {
  transition: transform .5s var(--ov-ease), box-shadow .5s var(--ov-ease);
  transform-style: preserve-3d;
}
@media (hover: hover) and (pointer: fine) {
  .ov-card--signal:hover {
    transform: translateY(-6px) rotate(-.4deg);
    box-shadow: 0 48px 90px -50px rgba(91, 49, 214, .85);
  }
}

/* The cases and the tiers had no hover at all while everything around them
   did, which made them read as pictures of cards rather than as cards. */
.ov-case { transition: transform .45s var(--ov-ease), border-color .45s var(--ov-ease); }
@media (hover: hover) and (pointer: fine) {
  .ov-case:hover { transform: translateY(-5px); border-color: rgba(255, 255, 255, .26); }
  .ov-case--feature:hover { border-color: transparent; }
}

/* One easing across the page, and a touch of scale on the way in, so the
   sections arrive as one system instead of eight separate fades. */
.ov-rise {
  transform: translateY(26px) scale(.995);
  transition:
    opacity .8s var(--ov-ease),
    transform .9s var(--ov-ease);
}
.ov-rise.is-visible { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .ov-chart__spark { animation: none; opacity: 0; }
  .ov-h1.ov-wipe { clip-path: none; }
}

/* ==========================================================================
   15. Arrival, and the form
   ========================================================================== */

/* The stagger writes --ov-delay onto each card in a grid. Without this line it
   was writing it to nobody: every card in a grid arrived on the same frame,
   which reads as the page popping rather than assembling. Only the cards below
   the fold use it — the hero's own stagger is set inline as the page loads. */
.ov-rise { transition-delay: var(--ov-delay, 0ms); }

/* The form is the one place on this site where somebody types, and it had a
   focus ring and nothing else: no way to see which field is wrong, and a
   submit button that stayed dead after a successful send. */
.ov-field {
  transition: border-color .25s var(--ov-ease), box-shadow .25s var(--ov-ease), background .25s var(--ov-ease);
}
.ov-field:focus-within {
  border-color: var(--ov-violet);
  box-shadow: 0 0 0 3px rgba(91, 49, 214, .16);
}

.ov-field--error {
  border-color: var(--ov-coral);
  background: rgba(255, 122, 107, .06);
}
.ov-field--error:focus-within {
  border-color: var(--ov-coral);
  box-shadow: 0 0 0 3px rgba(255, 122, 107, .2);
}
.ov-field--error .ov-field__label { color: #d8452f; }

.ov-formstatus {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ov-violet);
}
.ov-formstatus::before {
  content: "";
  flex: none;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--ov-violet) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 13 4 4 10-10'/%3E%3C/svg%3E") center/11px no-repeat;
}
.ov-formstatus--error { color: #d8452f; }
.ov-formstatus--error::before {
  background: #d8452f url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.5' stroke-linecap='round'%3E%3Cpath d='M12 7v6M12 17h.01'/%3E%3C/svg%3E") center/11px no-repeat;
}

/* A moment of confirmation on the panel itself, so the answer is not only a
   line of text under the button. */
.ov-form.is-sent { position: relative; }
.ov-form.is-sent .ov-field { border-color: rgba(91, 49, 214, .3); }
.ov-form.is-sent .ov-btn--field { background: var(--ov-violet); }

@media (prefers-reduced-motion: reduce) {
  .ov-rise { transition-delay: 0ms !important; }
}

/* ==========================================================================
   16. Bento hover
   Eight cards in four colours, so one hover cannot serve them all: white
   light on the coloured and dark cards, violet light on the pale ones, and a
   shadow tinted to whatever the card itself is made of.
   ========================================================================== */
.ov-tile,
.ov-bento__flag {
  transition:
    transform .45s var(--ov-ease),
    box-shadow .45s var(--ov-ease),
    background .45s var(--ov-ease);
}

/* The light that follows the pointer. The two coordinates come from the
   script; the gradient itself lives here, so the shape and the colour are a
   design decision rather than something buried in JavaScript. */
.ov-tile::after,
.ov-bento__flag::after,
.ov-step::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  opacity: 0;
  transition: opacity .4s var(--ov-ease);
  background: radial-gradient(
    280px circle at var(--mx, 50%) var(--my, 50%),
    rgba(255, 255, 255, .34),
    rgba(255, 255, 255, .08) 42%,
    transparent 68%
  );
}
/* On the pale cards white light is invisible, so theirs is violet. */
.ov-tile--pale::after,
.ov-tile--lilac::after,
.ov-step--1::after,
.ov-step--2::after {
  background: radial-gradient(
    280px circle at var(--mx, 50%) var(--my, 50%),
    rgba(91, 49, 214, .16),
    rgba(91, 49, 214, .05) 42%,
    transparent 68%
  );
}
.ov-tile { position: relative; }

@media (hover: hover) and (pointer: fine) {
  .ov-tile:hover::after,
  .ov-bento__flag:hover::after,
  .ov-step:hover::after { opacity: 1; }

  .ov-tile:hover        { transform: translateY(-6px); }
  .ov-bento__flag:hover { transform: translateY(-6px); }

  /* Each shadow carries its own card's colour rather than a generic grey. */
  .ov-tile--pale:hover   { box-shadow: 0 26px 50px -26px rgba(91, 49, 214, .38); }
  .ov-tile--lilac:hover  { box-shadow: 0 26px 50px -26px rgba(139, 92, 246, .5); }
  .ov-tile--coral:hover  { box-shadow: 0 26px 52px -24px rgba(255, 122, 107, .62); }
  .ov-tile--violet:hover { box-shadow: 0 26px 52px -24px rgba(139, 92, 246, .62); }
  .ov-tile--ink:hover    { box-shadow: 0 26px 52px -24px rgba(27, 16, 53, .6); }
  .ov-bento__flag:hover  { box-shadow: 0 34px 64px -28px rgba(91, 49, 214, .75); }

  /* Small answers inside the card, so the hover is the card reacting and not
     just the card moving. */
  .ov-tile__icon { transition: transform .45s var(--ov-ease); }
  .ov-tile:hover .ov-tile__icon { transform: translateY(-3px) scale(1.06); }

  .ov-tile__mark,
  .ov-tile__figure { transition: transform .5s var(--ov-ease), color .45s var(--ov-ease); }
  .ov-tile:hover .ov-tile__mark   { transform: translateX(4px); color: rgba(91, 49, 214, .34); }
  .ov-tile:hover .ov-tile__figure { transform: translateY(-2px); }

  .ov-bars__b { transition: transform .5s var(--ov-ease); transform-origin: bottom; }
  .ov-tile--ink:hover .ov-bars__b--1 { transform: scaleY(1.25); }
  .ov-tile--ink:hover .ov-bars__b--2 { transform: scaleY(1.14); }
  .ov-tile--ink:hover .ov-bars__b--3 { transform: scaleY(1.06); }

  .ov-bento__orb { transition: transform .8s var(--ov-ease), opacity .5s var(--ov-ease); }
  .ov-bento__flag:hover .ov-bento__orb { transform: scale(1.12); opacity: .85; }

  .ov-bento__band { transition: background .35s var(--ov-ease), border-color .35s var(--ov-ease), transform .35s var(--ov-ease); }
  .ov-bento__band:hover { transform: translateY(-3px); }
}

@media (prefers-reduced-motion: reduce) {
  .ov-tile:hover, .ov-bento__flag:hover, .ov-bento__band:hover { transform: none; }
  .ov-tile::after, .ov-bento__flag::after, .ov-step::after { display: none; }
}

/* ==========================================================================
   17. Glass
   Everything that was still a flat white surface becomes translucent, so the
   ground — its light, its drift, its lattice — carries through the whole page
   instead of being interrupted by opaque bands.

   The full-width bands take no blur, deliberately. backdrop-filter over an
   area that size is one of the most expensive things a browser can be asked
   to do on every scroll frame, and there is nothing behind them to blur: the
   ground is a smooth gradient and a 34px dot grid. Translucency alone gives
   the same result for free. The blur is spent where something detailed sits
   behind and the surface is small — the cards.
   ========================================================================== */
:root {
  --ov-band:      rgba(255, 255, 255, .62);
  --ov-band-soft: rgba(255, 255, 255, .5);
}

.ov-services,
.ov-partners,
.ov-contact,
.ov-marquee { background: var(--ov-band); }

/* The waves have to be made of the same glass as the bands they join, or the
   seams come back exactly where they were before. Both sit over one continuous
   ground, so identical translucency composites to an identical colour. */
/* The hero's wave is the one drawn upside down — it sits over the section and
   paints what is below the curve, so the glass belongs to the fill and the
   band itself has to stay clear. Left as it was, the band's own glass covered
   the photograph in a straight line a hundred pixels above the wave, which is
   exactly the cut the inversion was meant to remove. */
.ov-curve--hero    { background: transparent; }
.ov-curve--hero .ov-curve__fill {
  /* Opaque, unlike every other wave here. The others have nothing behind them
     but the page's ground, so glass over it is glass over the same thing the
     next section shows. This one has the photograph behind it — left
     translucent, the picture read straight through the wave and then stopped
     dead at the section's last pixel, a 36-point step across the full width.
     Flattened, it is the band's own colour with nothing showing through. */
  fill: #fcfbff;
  fill: color-mix(in srgb, #fff 62%, var(--ov-bg));
}

.ov-curve--travel,
.ov-curve--about   { background: var(--ov-band); }
.ov-curve--services,
.ov-curve--partners { background: transparent; }
.ov-curve--services .ov-curve__fill,
.ov-curve--partners .ov-curve__fill { fill: var(--ov-band); }

/* The footer's wave is the one with a dark band under it, and glass over ink
   is not glass — it is grey. Measured on the published page: the ground above
   the wave is (249,247,253) and the shape meant to continue it was arriving
   at (168,164,178), a flat grey stripe across the full width. Opaque, and the
   join is invisible: --ov-bg lands within two points of the ground above it.
   The other two waves sit over the page's own pale ground, where the same
   translucency does no harm. */
.ov-curve--footer .ov-curve__fill { fill: var(--ov-bg); }
.ov-curve--travel .ov-curve__fill,
.ov-curve--about  .ov-curve__fill { fill: transparent; }

/* The pale cards. Small enough that a real blur is worth its cost, and they
   sit over the lattice, so there is something behind them to soften. */
.ov-tile--pale {
  background: rgba(255, 255, 255, .58);
  backdrop-filter: blur(16px) saturate(1.2);
  border-color: rgba(255, 255, 255, .85);
}
.ov-tile--lilac {
  background: rgba(237, 228, 255, .72);
  backdrop-filter: blur(16px) saturate(1.2);
}
/* :not() on the feature tier, because this rule sits after the one that fills
   it violet and carries the same weight — without it, the tier the section is
   built to push turns into glass like the other two and stops being the
   answer to the question the section asks. */
.ov-bento__band,
.ov-tier:not(.ov-tier--feature) {
  background: rgba(255, 255, 255, .55);
  backdrop-filter: blur(18px) saturate(1.2);
  border-color: rgba(255, 255, 255, .8);
}

/* The form's own fields were the last flat white on the page, sitting inside
   a panel that is already glass — a solid rectangle inside a translucent one. */
.ov-field {
  background: rgba(255, 255, 255, .55);
  backdrop-filter: blur(14px);
  border-color: rgba(255, 255, 255, .85);
}
.ov-field:focus-within { background: rgba(255, 255, 255, .78); }

/* Backdrop filters are dropped on iOS and cost most on the machines least able
   to pay, so on touch the cards fall back to a flatter, more opaque fill. The
   bands never had a blur to lose. */
@media (hover: none) {
  .ov-tile--pale,
  .ov-bento__band,
  .ov-tier:not(.ov-tier--feature),
  .ov-field { backdrop-filter: none; background: rgba(255, 255, 255, .82); }
  .ov-tile--lilac { backdrop-filter: none; background: rgba(237, 228, 255, .9); }
}

/* ==========================================================================
   18. Glass on the travel page
   Its sections are already transparent and most of its cards are already
   glass. What was left opaque were two tint layers — solid panels laid under
   a translucent body so that whatever sat behind could not show through. That
   was written when the thing behind was a photograph. What sits behind now is
   the page's own ground, which is exactly what should show through.
   ========================================================================== */
.ov-gpanel__tint,
.ov-acc__tint { background: rgba(244, 246, 255, .5); }

/* Translucent already, but with nothing softening what it lets through. */
.ov-trustbar,
.ov-bcard {
  backdrop-filter: blur(18px) saturate(1.2);
}

@media (hover: none) {
  .ov-gpanel__tint,
  .ov-acc__tint { background: rgba(244, 246, 255, .82); }
  .ov-trustbar,
  .ov-bcard { backdrop-filter: none; }
}

/* ==========================================================================
   19. Loading placeholders
   The page itself needs none: it is server-rendered, and with JavaScript off
   every element is visible — checked, 42 entrance elements, none left at zero
   opacity. A skeleton over text that is already in the HTML would be theatre.

   The photographs are the exception. They are lazy-loaded, their containers
   already reserve height so nothing shifts, but on a slow connection that
   reserved space is a transparent hole. This fills it.
   ========================================================================== */
.ov-mem__media,
.ov-bcard__media,
.ov-tcard__media,
.ov-getaway__media,
.ov-ppanel__media,
.ov-join__bg,
.ov-secbg {
  /* No position here. Every one of these already declares its own — the two
     panels sit relative in the flow, the four card medias and the join
     background are absolute and inset to cover their card. Setting relative
     for all of them clobbered that: the photograph dropped back into the flow
     and took a block at its own aspect ratio instead of filling the card, so
     the glass panel ended up beside the picture rather than over it. The
     shimmer only needs a positioned ancestor, and it already has one. */
  background-color: rgba(139, 92, 246, .07);
}

/* The shimmer sits behind the image, never over it: once the photograph paints
   it is opaque and covers this by itself. If the script never runs, nothing is
   hidden and nothing is broken — the placeholder simply stays behind a picture
   that is already there. */
.ov-mem__media::before,
.ov-bcard__media::before,
.ov-tcard__media::before,
.ov-getaway__media::before,
.ov-ppanel__media::before,
.ov-join__bg::before,
.ov-secbg::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    linear-gradient(100deg,
      rgba(255, 255, 255, 0) 20%,
      rgba(255, 255, 255, .55) 42%,
      rgba(255, 255, 255, 0) 64%),
    rgba(196, 181, 253, .18);
  background-size: 220% 100%, 100% 100%;
  animation: ovShimmer 1.9s ease-in-out infinite;
}
@keyframes ovShimmer {
  from { background-position: 120% 0, 0 0; }
  to   { background-position: -60% 0, 0 0; }
}

/* Stopped once the photograph has arrived, so nothing animates behind an
   opaque image for the life of the page. */
.is-loaded::before { animation: none; opacity: 0; }

.ov-mem__media img,
.ov-bcard__media img,
.ov-tcard__media img,
.ov-getaway__media img,
.ov-ppanel__media img,
.ov-join__bg img,
.ov-secbg img { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .ov-mem__media::before, .ov-bcard__media::before, .ov-tcard__media::before,
  .ov-getaway__media::before, .ov-ppanel__media::before, .ov-join__bg::before,
  .ov-secbg::before { animation: none; }
}

/* ==========================================================================
   20. The glass that was not glass
   ========================================================================== */

/* Twenty-six surfaces on this site asked the browser to blur what was behind
   them. Six of them sit over the hero photograph, where that is the whole
   point. The other twenty sit over the page's own ground — a smooth gradient
   with a fine dot lattice — and blurring that produces, pixel for pixel, what
   was already there.

   Measured rather than assumed: with these turned off, a full-page comparison
   of both pages came back with a maximum difference of zero across eleven
   million pixels. Not "close enough" — identical.

   So they stop asking. The look is unchanged and the compositor has twenty
   fewer backdrop captures to make on every frame. The header keeps its own,
   because the page really does move behind it. */
#ov-main > section:not(.ov-hero),
#ov-main > section:not(.ov-hero) *,
#ov-main > div,
#ov-main > div * {
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}

/* …except where something is actually behind them.

   The paragraph above was written when every one of these sat over a flat
   ground and blurring it returned the same pixels — measured, a maximum
   difference of zero across eleven million. That stopped being true when the
   embossed mark and the rising line went in behind the process cards, and it
   was never true of the cards over the hero photograph.

   So the glass comes back where there is something to see through it, at a
   radius chosen for cost rather than habit: 26px was the old figure and most
   of the expense. */
#ov-main section .ov-svc,
#ov-main section .ov-bento__band,
#ov-main section .ov-step,
#ov-main section .ov-proof,
.ov-card--signal,
.ov-card--cac,
.ov-note,
.ov-hero__panel {
  /* Matched to the specificity of the rule that switches them off, which is
     an id, a class and a type — the :not(.ov-hero) counts as a class and the
     section counts as a type. Two attempts lost to it before that was
     counted properly; the glass surfaces stayed at six both times. */
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
          backdrop-filter: blur(12px) saturate(1.2);
}

/* ==========================================================================
   21. The scrollbar
   ========================================================================== */

/* The page's own bar, in the page's own colours.

   Two layers, because no single mechanism covers everything. The standard
   properties are all Firefox has and all that Chrome needs to behave; the
   -webkit- rules are what make it look like this site rather than like a
   tinted default — a gradient thumb, rounded, on a track that lets the
   background through.

   The cost, stated plainly: styling ::-webkit-scrollbar takes the browser out
   of overlay scrollbars. On macOS, where the bar normally fades in only while
   you scroll, it becomes a slim permanent one. Ten pixels and a transparent
   track is as light as that can be made. On Windows, which is where this
   site's readers overwhelmingly are, the bar was always there anyway and this
   simply makes it the brand's. */
/* Firefox only, and deliberately fenced off.

   These two cannot simply sit beside the -webkit- rules: from Chrome 121, a
   scroller that specifies scrollbar-color or scrollbar-width ignores the
   ::-webkit-scrollbar pseudo-elements entirely. Declared together, the flat
   two-colour version silently won and everything below did nothing — the bar
   came out 15px wide in the default shape, which is how this was caught.

   Firefox does not support the selector() test, so it takes this branch;
   Chrome and Safari skip it and get the real thing. */
@supports not selector(::-webkit-scrollbar) {
  html {
    scrollbar-width: thin;
    scrollbar-color: rgba(91, 49, 214, .45) transparent;
  }
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track { background: transparent; }

/* The border with padding-box clipping is what keeps the thumb off the very
   edge: a bar flush to the window looks like part of the chrome, one with air
   around it looks like part of the page. */
::-webkit-scrollbar-thumb {
  border: 2px solid transparent;
  border-radius: 999px;
  background: linear-gradient(180deg, var(--ov-violet-soft), var(--ov-violet) 55%, var(--ov-coral));
  background-clip: padding-box;
}

::-webkit-scrollbar-thumb:hover {
  border-width: 1px;
  background: linear-gradient(180deg, var(--ov-violet), var(--ov-violet-deep) 55%, #ff5a45);
  background-clip: padding-box;
}

::-webkit-scrollbar-corner { background: transparent; }
   reduced-motion block either. */

/* ==========================================================================
   22. Services — the bento, rebuilt
   ========================================================================== */

/* Twelve columns, because the design's proportions do not survive four.

   The heading is not above the grid, it is in it: the top-left cell. That is
   what lets card 02 rise to the top of the section beside it, and card 01 sit
   under the heading at half width and two rows tall. Written as a heading plus
   a separate grid, the right-hand column has to start below the title and the
   whole arrangement collapses into rows of equal tiles. */
/* Three classes deep on purpose. The travel page's savings grid answers to a
   bare .ov-bento that also happens to be twelve columns, and with two classes
   this rule kept losing its gap and row sizing to it in a way the cascade says
   should not happen. Rather than keep arguing with it, this one is specific
   enough that the question does not arise. */
.ov-services .ov-wrap .ov-bento {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: clamp(12px, 1.2vw, 18px);
  grid-auto-rows: auto;
  align-items: stretch;
}

.ov-bento__head { grid-column: 1 / span 6; grid-row: 1; margin-bottom: 0; align-self: start; }
.ov-bento__head .ov-h2 { margin-bottom: 0; }

/* The row spans are what make the shape. 02 climbs beside the heading and
   comes down level with the flagship's middle; 01 starts under the heading
   and runs to the foot of 03 and 04. */
/* The flagship is the tallest thing here, and 02 is taller than the pair
   below it. Without a floor of their own every card ends up the height of the
   shortest one, which is the look this grid exists to avoid. */
.ov-svc--flag  { grid-column: 1 / span 6;  grid-row: 2 / span 2; min-height: 400px; }
.ov-svc--02    { grid-column: 7 / span 6;  grid-row: 1 / span 2; min-height: 320px; }
.ov-svc--coral { grid-column: 7 / span 3;  grid-row: 3; min-height: 250px; }
.ov-svc--lilac { grid-column: 10 / span 3; grid-row: 3; min-height: 250px; }
.ov-svc--05    { grid-column: 1 / span 4;  grid-row: 4; min-height: 270px; }
.ov-svc--ink   { grid-column: 5 / span 4;  grid-row: 4; min-height: 270px; }
.ov-svc--07    { grid-column: 9 / span 4;  grid-row: 4; min-height: 270px; }
.ov-bento__band { grid-column: 1 / span 12; grid-row: 5; }

/* --------------------------------------------------------------------------
   The card
   -------------------------------------------------------------------------- */

.ov-svc {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 210px;
  padding: clamp(18px, 1.7vw, 26px);
  border-radius: clamp(16px, 1.5vw, 22px);
  overflow: hidden;
  isolation: isolate;
  background: var(--ov-band);
  border: 1px solid var(--ov-glass-brd);
  transition: transform .5s cubic-bezier(.2, .7, .2, 1), box-shadow .5s ease;
}
.ov-svc:hover { transform: translateY(-4px); box-shadow: 0 30px 60px -40px rgba(50, 28, 120, .6); }

.ov-svc__n {
  position: relative;
  z-index: 2;
  font-family: var(--ov-font-display);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .04em;
  color: rgba(27, 16, 53, .38);
}

/* The body takes the slack, so every card's link sits on its own baseline at
   the foot however tall the card happens to be. */
.ov-svc__body { position: relative; z-index: 2; margin-top: 14px; flex: 1 1 auto; }

.ov-svc__title {
  margin: 0;
  max-width: 12ch;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(20px, 1.9vw, 27px);
  line-height: 1.12;
  letter-spacing: -.03em;
  color: var(--ov-ink);
}
.ov-svc__title--xl { max-width: 9ch; font-size: clamp(30px, 3.4vw, 48px); }
.ov-svc__title--sm { max-width: 18ch; font-size: 15px; font-weight: 600; letter-spacing: -.01em; }

.ov-svc__sub {
  margin: 14px 0 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ov-body-soft);
}

.ov-svc__figure {
  margin: 0 0 6px;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(30px, 3vw, 42px);
  line-height: 1;
  letter-spacing: -.045em;
  color: var(--ov-violet);
}

.ov-svc__go {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
  align-self: flex-start;
  font-size: 14px;
  font-weight: 600;
  color: var(--ov-violet);
  text-decoration: none;
}
.ov-svc__arrow { transition: transform .35s cubic-bezier(.2, .7, .2, 1); }
.ov-svc__go:hover .ov-svc__arrow,
.ov-svc:hover .ov-svc__arrow { transform: translateX(5px); }

/* --------------------------------------------------------------------------
   Tones
   -------------------------------------------------------------------------- */

.ov-svc--flag {
  background: linear-gradient(122deg, #5b31d6 0%, #4b46e6 46%, #2f7de0 100%);
  border-color: transparent;
  box-shadow: 0 40px 80px -54px rgba(59, 43, 214, .9);
}
.ov-svc--flag .ov-svc__n     { color: rgba(255, 255, 255, .6); }
.ov-svc--flag .ov-svc__title { color: #fff; }
.ov-svc--flag .ov-svc__sub   { color: rgba(255, 255, 255, .78); }
.ov-svc--flag .ov-svc__go    { color: #fff; }

/* The mark, blown up and half-submerged. Not a logo here — a watermark the
   card is printed on. */
.ov-svc__eye {
  position: absolute;
  right: -6%;
  top: 50%;
  translate: 0 -50%;
  width: min(58%, 420px);
  opacity: .17;
  z-index: 1;
  pointer-events: none;
}
.ov-svc__eye img { width: 100%; height: auto; filter: brightness(0) invert(1); }

.ov-svc--coral {
  background: linear-gradient(140deg, #ff8168, #f2624f);
  border-color: transparent;
}
.ov-svc--coral .ov-svc__n     { color: rgba(255, 255, 255, .72); }
.ov-svc--coral .ov-svc__title { color: #fff; }
.ov-svc--coral .ov-svc__go    { color: #fff; }

.ov-svc--lilac { background: linear-gradient(150deg, rgba(237, 233, 254, .95), rgba(224, 231, 255, .9)); }

.ov-svc--ink {
  background: linear-gradient(150deg, #171033, #0f0a22);
  border-color: transparent;
}
.ov-svc--ink .ov-svc__n     { color: rgba(255, 255, 255, .45); }
.ov-svc--ink .ov-svc__title { color: #fff; }
.ov-svc--ink .ov-svc__go    { color: var(--ov-violet-pale); }

/* The badge on the figure card. */
.ov-svc__badge {
  position: absolute;
  right: clamp(18px, 1.7vw, 26px);
  top: clamp(18px, 1.7vw, 26px);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 13px;
  color: var(--ov-violet);
  background: rgba(255, 255, 255, .85);
}
.ov-svc__badge .ov-icon { width: 21px; height: 21px; }

/* --------------------------------------------------------------------------
   The illustrations
   --------------------------------------------------------------------------
   All decorative, all hidden from assistive technology: every card is named
   by its heading, so none of this carries information a reader would miss.
   They sit under the copy, never over it. */

.ov-svc__art { position: absolute; z-index: 1; pointer-events: none; }

/* 02 — the climb. Dotted where it is history, solid where it is now. */
.ov-art--chart { right: 0; bottom: 0; width: 60%; max-width: 420px; }
.ov-art--chart svg { width: 100%; height: auto; display: block; }
.ov-art__bars rect { fill: rgba(91, 49, 214, .07); }
.ov-art__nodes circle { fill: #fff; stroke: #5b31d6; stroke-width: 2.4; }
.ov-art__nodes circle:last-child { stroke-width: 3; }


/* 05 — a browser, reduced to the parts you recognise it by. */
.ov-art--browser {
  right: 5%; bottom: 12%;
  width: 52%; max-width: 250px;
  padding: 9px;
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 20px 40px -28px rgba(50, 28, 120, .5);
}
.ov-art__chrome { display: flex; gap: 5px; margin-bottom: 8px; }
.ov-art__chrome i { width: 7px; height: 7px; border-radius: 50%; background: rgba(27, 16, 53, .18); }
.ov-art__screen { display: block; border-radius: 8px; overflow: hidden; line-height: 0; }
.ov-art__screen svg { width: 100%; height: 74px; display: block; }
.ov-art__row { display: flex; gap: 6px; margin-top: 8px; }
.ov-art__row i { flex: 1; height: 22px; border-radius: 6px; background: rgba(139, 92, 246, .18); }


/* 07 — reach, as rings going out from a point. */
.ov-art--reach { right: -14%; top: -6%; width: 62%; height: 100%; }
.ov-art--reach svg { width: 100%; height: 100%; display: block; }
.ov-art__rings circle { fill: none; stroke: rgba(91, 49, 214, .16); stroke-width: 1.4; }
.ov-art__ad {
  position: absolute;
  right: 22%; bottom: 12%;
  display: grid;
  gap: 6px;
  width: 46%;
  padding: 11px;
  border-radius: 10px;
  background: #fff;
  box-shadow: 0 18px 36px -26px rgba(50, 28, 120, .55);
}
.ov-art__ad i { height: 8px; border-radius: 4px; background: rgba(139, 92, 246, .22); }
.ov-art__ad i:first-child { height: 26px; background: rgba(139, 92, 246, .3); }
.ov-art__cursor { position: absolute; right: 16%; bottom: 4%; }
.ov-art__cursor svg { width: 26px; height: 26px; fill: var(--ov-ink); }

/* --------------------------------------------------------------------------
   The line under the grid
   -------------------------------------------------------------------------- */

.ov-bento__band {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 26px);
  padding: 14px clamp(18px, 1.8vw, 26px);
  border-radius: 999px;
  background: var(--ov-band);
  border: 1px solid var(--ov-glass-brd);
}
.ov-bento__mark {
  display: grid;
  place-items: center;
  flex: none;
  width: 48px; height: 48px;
  border-radius: 14px;
  background: var(--ov-violet);
}
.ov-bento__mark img { width: 26px; height: auto; filter: brightness(0) invert(1); }
.ov-bento__bandtitle { margin: 0; font-size: 15px; font-weight: 600; color: var(--ov-ink); }
.ov-bento__rule { flex: 1 1 auto; height: 1px; background: var(--ov-rule); }
.ov-svc__go--band { margin-top: 0; }

/* --------------------------------------------------------------------------
   Services — the corrections that only showed up once it was drawn
   -------------------------------------------------------------------------- */

/* The drawn rule under the section's headline is violet here, not coral. The
   hero's is coral because coral marks the one thing worth looking at first on
   that screen; on this one that is the grid, not the title. */
.ov-mark--violet,
.ov-mark--violet .ov-mark__rule { color: var(--ov-violet); }

/* Copy keeps its own half of the card. The illustrations sit under the text in
   the stacking order, so nothing was ever hidden — but a heading printed over
   a picture of a browser is still a heading you have to work to read. */
.ov-svc--02 .ov-svc__body,
.ov-svc--05 .ov-svc__body,
.ov-svc--07 .ov-svc__body { padding-right: 46%; }
.ov-svc--coral .ov-svc__body { padding-right: 34%; }
.ov-svc--ink .ov-svc__body   { padding-right: 42%; }

.ov-svc--05 .ov-svc__title,
.ov-svc--ink .ov-svc__title { max-width: 9ch; }


/* 05 — the browser drops clear of the two-line heading. */
.ov-art--browser { right: 6%; bottom: 16%; width: 46%; }


/* 07 — the rings were running off the card and taking the advert with them. */
.ov-art--reach { right: -6%; top: -4%; width: 52%; height: 92%; }
.ov-art__ad { right: 14%; bottom: 14%; width: 40%; }
.ov-art__cursor { right: 8%; bottom: 6%; }

/* --------------------------------------------------------------------------
   Services — narrower screens
   --------------------------------------------------------------------------
   The twelve-column arrangement is a composition, not a grid that reflows: its
   whole point is that 02 rises beside the heading and 01 sits under it at two
   rows tall. Below the width that holds, the explicit placements have to be
   released or they keep forcing six-column cards into a six-column page. */

@media (max-width: 1100px) {
  .ov-services .ov-wrap .ov-bento { grid-template-columns: repeat(6, minmax(0, 1fr)); }

  .ov-bento__head,
  .ov-svc,
  .ov-bento__band { grid-column: 1 / -1; grid-row: auto; }

  .ov-svc--coral,
  .ov-svc--lilac,
  .ov-svc--05,
  .ov-svc--ink,
  .ov-svc--07 { grid-column: span 3; }

  .ov-svc--flag { min-height: 320px; }
  .ov-svc--02   { min-height: 260px; }
}

@media (max-width: 720px) {
  .ov-services .ov-wrap .ov-bento { grid-template-columns: 1fr; }
  .ov-svc { grid-column: 1 / -1; min-height: 0; }

  /* One column is not wide enough to keep copy and picture apart, and a
     heading printed over an illustration is not worth the illustration. The
     flagship's watermark stays: it is behind the whole card, not beside the
     words. */
  .ov-svc__body { padding-right: 0; }
  .ov-svc__art  { display: none; }
  .ov-svc__title,
  .ov-svc--05 .ov-svc__title,
  .ov-svc--ink .ov-svc__title { max-width: 16ch; }

  .ov-bento__band {
    flex-wrap: wrap;
    border-radius: clamp(16px, 4vw, 22px);
  }
  .ov-bento__rule { display: none; }
}

/* --------------------------------------------------------------------------
   Services — the two drawn illustrations
   --------------------------------------------------------------------------
   Cards 03 and 06 carry artwork rather than shapes built out of divs. They
   bleed off the card's right edge on purpose: contained, they read as a
   picture pasted on, and overflowing they read as something the card is a
   window onto. */

.ov-art--social,
.ov-art--reel {
  right: -8%;
  top: 50%;
  translate: 0 -50%;
  bottom: auto;
  width: 58%;
  height: auto;
}
.ov-art--social img,
.ov-art--reel img {
  width: 100%;
  height: auto;
  display: block;
}

/* The dark card's artwork was drawn on black, so its own shadows fall into the
   card's ground. On the coral card there is nothing to fall into, which is why
   only that one needs lifting off its background. */
.ov-art--social img { filter: drop-shadow(0 18px 30px rgba(120, 30, 20, .28)); }

/* Size and place, after seeing them on the cards. The earlier positions were
   written for shapes built out of divs, which sat where they were told; a
   picture needs room to bleed and the copy needs its half back. */
.ov-svc--coral .ov-svc__body,
.ov-svc--ink .ov-svc__body { padding-right: 48%; }

.ov-art--social {
  right: -7%;
  top: auto;
  bottom: -12%;
  translate: none;
  width: 60%;
}
.ov-art--reel {
  right: -5%;
  top: auto;
  bottom: -10%;
  translate: none;
  width: 58%;
}

/* The two are placed differently on purpose.

   Centring was tried on both: it holds better at narrow widths, where the
   bleed drops the artwork into a corner and opens a wedge of empty colour
   above it. On the coral card the bleed is the better picture anyway, so that
   one keeps it and the dark card takes the centred version. */
.ov-art--reel {
  right: 3%;
  bottom: auto;
  top: 50%;
  translate: 0 -50%;
  width: 52%;
}

.ov-art--social {
  right: -7%;
  top: auto;
  bottom: -12%;
  translate: none;
  width: 60%;
}

/* --------------------------------------------------------------------------
   03 — the feed, built out of shapes again
   --------------------------------------------------------------------------
   The drawn illustration went in and came back out: at the size this card
   gives it, the built version reads more clearly than a picture of the same
   thing shrunk down. Its placement is the one it always had — to the right of
   the words, clear of the link. */

.ov-art--social {
  right: -4%;
  top: 16%;
  bottom: auto;
  translate: none;
  width: 46%;
  height: 66%;
}
.ov-art__post {
  position: absolute;
  border-radius: 14px;
  background: rgba(255, 255, 255, .3);
  border: 1px solid rgba(255, 255, 255, .45);
}
.ov-art__post--back  { right: 6%;  top: 4%;  width: 62%; height: 74%; }
.ov-art__post--front { right: 24%; top: 20%; width: 62%; height: 76%; background: rgba(255, 255, 255, .42); }
.ov-art__heart {
  position: absolute;
  right: 10%; top: 0;
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  border-radius: 12px;
  color: #f2624f;
  background: #fff;
}
.ov-art__heart .ov-icon { width: 19px; height: 19px; }
.ov-art__pill {
  position: absolute;
  left: -14%; top: 52%;
  padding: 7px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  color: #f2624f;
  background: #fff;
}

/* ==========================================================================
   23. Process — the staircase, rebuilt
   ========================================================================== */

/* "momentum." runs violet to coral, and its rule with it. A flat colour would
   have been a word in a different colour; the gradient is the section's own
   argument said in one word — it starts where the first card starts and ends
   where the last one does. */
.ov-mark--warm {
  background: linear-gradient(96deg, #7c5cf5 0%, #a855f7 42%, #ff5a45 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}

.ov-arc { position: relative; margin-top: clamp(34px, 4vw, 62px); }

.ov-arc__line { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.ov-arc__line svg { width: 100%; height: 100%; overflow: visible; }
/* Held until the section arrives. See ov-reveal on .ov-arc__line: the draw
   used to start at page load and finish two and a half thousand pixels before
   anyone could see it. */
.ov-arc__path {
  stroke-dasharray: 1600;
  stroke-dashoffset: 1600;
}
/* No timed animation here: drawOnScroll() sets the offset directly, so a
   keyframe would only fight it. */

/* Nodes as elements, not as circles in the line's own SVG: that one is
   stretched to the section's box, and a circle stretched with it is an
   ellipse. */
.ov-arc__node {
  opacity: 0;
  position: absolute;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: var(--ov-violet);
  box-shadow: 0 0 0 4px rgba(255, 255, 255, .9), 0 6px 14px -6px rgba(50, 28, 120, .7);
}
.ov-arc__line.is-visible .ov-arc__node {
  animation: ovFadeIn .6s ease-out 1.6s both;
}
.ov-arc__node--end {
  width: 17px; height: 17px;
  margin: -8.5px 0 0 -8.5px;
  background: #ff4d36;
}

/* The climb itself: six columns on one baseline, each card lifted further off
   it than the last. The lift is the argument, so it is layout. */
.ov-arc__steps {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 16px);
  align-items: end;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ov-step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: clamp(14px, 1.3vw, 20px);
  border-radius: clamp(14px, 1.3vw, 20px);
  background: var(--ov-band);
  border: 1px solid var(--ov-glass-brd);
  box-shadow: 0 24px 50px -38px rgba(50, 28, 120, .55);
}

/* The lift. Six values rather than a formula, because the last one breaks the
   pattern on purpose: Grow stands a step higher than the climb would put it. */
.ov-step--1 { margin-bottom: 0; }
.ov-step--2 { margin-bottom: 60px; }
.ov-step--3 { margin-bottom: 120px; }
.ov-step--4 { margin-bottom: 182px; }
.ov-step--5 { margin-bottom: 244px; }
.ov-step--6 { margin-bottom: 318px; }

.ov-step__n {
  font-family: var(--ov-font-display);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .04em;
  color: var(--ov-violet);
}
.ov-step__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 14px;
  color: var(--ov-violet);
  background: rgba(139, 92, 246, .12);
}
.ov-step__icon .ov-icon { width: 21px; height: 21px; }

.ov-step__title {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(17px, 1.5vw, 21px);
  line-height: 1.1;
  letter-spacing: -.02em;
  color: var(--ov-ink);
}
.ov-step__desc { font-size: 13.5px; line-height: 1.45; color: var(--ov-body-soft); }

.ov-step__figure {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(30px, 3vw, 42px);
  line-height: 1;
  letter-spacing: -.045em;
}

/* Each step a shade warmer than the one before it. */
.ov-step--2 { background: rgba(237, 233, 254, .85); }
.ov-step--3 { background: rgba(221, 214, 254, .9); }

.ov-step--4,
.ov-step--5,
.ov-step--6 { border-color: transparent; }

.ov-step--4 { background: linear-gradient(150deg, #7c5cf5, #6d43e0); }
.ov-step--5 { background: linear-gradient(150deg, #6236e4, #4b2ad4); }
.ov-step--6 { background: linear-gradient(150deg, #ff6b4a 0%, #f04f7a 46%, #6d43e0 100%); }

.ov-step--4 .ov-step__n,
.ov-step--5 .ov-step__n,
.ov-step--6 .ov-step__n { color: rgba(255, 255, 255, .72); }

.ov-step--4 .ov-step__title,
.ov-step--5 .ov-step__title,
.ov-step--6 .ov-step__title,
.ov-step--6 .ov-step__figure { color: #fff; }

.ov-step--4 .ov-step__desc,
.ov-step--5 .ov-step__desc,
.ov-step--6 .ov-step__desc { color: rgba(255, 255, 255, .78); }

.ov-step--4 .ov-step__icon,
.ov-step--5 .ov-step__icon,
.ov-step--6 .ov-step__icon { color: #fff; background: rgba(255, 255, 255, .18); }

.ov-step--6 { box-shadow: 0 40px 80px -46px rgba(240, 79, 122, .8); }

/* --------------------------------------------------------------------------
   The four claims
   -------------------------------------------------------------------------- */

.ov-proof {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 34px);
  margin-top: clamp(28px, 3.4vw, 52px);
  padding: clamp(18px, 2vw, 28px) clamp(20px, 2.2vw, 34px);
  border-radius: clamp(18px, 1.8vw, 26px);
  background: var(--ov-band);
  border: 1px solid var(--ov-glass-brd);
}
.ov-proof__item { display: flex; gap: 14px; align-items: flex-start; }
.ov-proof__item + .ov-proof__item {
  padding-left: clamp(16px, 2vw, 34px);
  border-left: 1px solid var(--ov-rule);
}
.ov-proof__badge {
  display: grid;
  place-items: center;
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  color: var(--ov-violet);
  background: rgba(237, 233, 254, .9);
}
.ov-proof__badge .ov-icon { width: 22px; height: 22px; }
.ov-proof__claim {
  margin: 0 0 6px;
  font-size: 14.5px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--ov-ink);
}
.ov-proof__note { margin: 0; font-size: 13.5px; line-height: 1.45; color: var(--ov-body-soft); }

/* --------------------------------------------------------------------------
   Process — narrower screens
   --------------------------------------------------------------------------
   The staircase is a picture of a climb, and a climb needs width. Below the
   width where six columns still hold a sentence, the cards go to three and
   then to one, the lift comes off, and the line goes with it — a rising line
   drawn behind a vertical stack is not rising, it is just a diagonal. */

@media (max-width: 1100px) {
  .ov-arc__steps { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: 18px; }
  .ov-arc__line { display: none; }

  .ov-step--1, .ov-step--2, .ov-step--3,
  .ov-step--4, .ov-step--5, .ov-step--6 { margin-bottom: 0; }

  .ov-proof { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 22px; }
  .ov-proof__item + .ov-proof__item { padding-left: 0; border-left: 0; }
  .ov-proof__item:nth-child(even) {
    padding-left: clamp(16px, 2vw, 34px);
    border-left: 1px solid var(--ov-rule);
  }
}

@media (max-width: 720px) {
  .ov-arc__steps { grid-template-columns: 1fr; }
  .ov-proof { grid-template-columns: 1fr; }
  .ov-proof__item:nth-child(even) { padding-left: 0; border-left: 0; }
  .ov-proof__item + .ov-proof__item {
    padding-top: 18px;
    border-top: 1px solid var(--ov-rule);
  }
}

/* --------------------------------------------------------------------------
   The mark, embossed
   --------------------------------------------------------------------------
   Pressed into the section rather than printed on it. The shape itself is
   filled with a whisper of white — near enough to the ground that it reads as
   the same surface — and the relief is two shadows: a highlight up and to the
   left, a shadow down and to the right. Colour it, and it stops being relief
   and becomes a watermark. */

.ov-emboss {
  position: absolute;
  left: 1%;
  top: 26%;
  z-index: 0;
  width: min(52%, 700px);
  aspect-ratio: 3 / 2;
  pointer-events: none;

  /* Deeper than the first attempt, which was too polite to see. The face is
     brighter than the ground, the highlight is a hard white edge with no blur
     at all — blur is what made it disappear — and the shadow is both larger
     and darker. */
  background: rgba(255, 255, 255, .62);

  -webkit-mask: url("../img/mark-silhouette.png") center / contain no-repeat;
          mask: url("../img/mark-silhouette.png") center / contain no-repeat;

  filter:
    drop-shadow(-3px -3px 0 rgba(255, 255, 255, 1))
    drop-shadow(-1px -1px 0 rgba(255, 255, 255, .9))
    drop-shadow(6px 8px 6px rgba(66, 40, 150, .34))
    drop-shadow(2px 3px 0 rgba(66, 40, 150, .16));
}

/* On a phone the mark would sit behind six stacked cards and read as dirt on
   the screen rather than as relief. */
@media (max-width: 720px) {
  .ov-emboss { display: none; }
}

/* Raised off the floor. Every step keeps its own place in the climb — the
   whole staircase simply stands a little higher in the section. */
.ov-step--1 { margin-bottom: 38px; }
.ov-step--2 { margin-bottom: 98px; }
.ov-step--3 { margin-bottom: 158px; }
.ov-step--4 { margin-bottom: 220px; }
.ov-step--5 { margin-bottom: 282px; }
.ov-step--6 { margin-bottom: 356px; }

@media (max-width: 1100px) {
  .ov-step--1, .ov-step--2, .ov-step--3,
  .ov-step--4, .ov-step--5, .ov-step--6 { margin-bottom: 0; }
}

/* ==========================================================================
   24. Interactions
   ==========================================================================
   Transform and opacity only. Everything here runs on hover or focus, so it
   costs nothing while the page is being read — which is the state the page is
   in almost all of the time. */

/* --------------------------------------------------------------------------
   The staircase answers back
   -------------------------------------------------------------------------- */

.ov-step {
  transition:
    transform .45s cubic-bezier(.2, .7, .2, 1),
    box-shadow .45s ease;
}
.ov-step__icon {
  transition:
    transform .45s cubic-bezier(.2, .7, .2, 1),
    background-color .35s ease;
}

@media (hover: hover) and (pointer: fine) {
  .ov-step:hover {
    transform: translateY(-6px);
    box-shadow: 0 34px 64px -34px rgba(50, 28, 120, .6);
  }
  /* The icon leans into the climb: up and to the right, the way the line goes. */
  .ov-step:hover .ov-step__icon { transform: translate(3px, -3px) rotate(-4deg); }
  .ov-step--1:hover .ov-step__icon,
  .ov-step--2:hover .ov-step__icon,
  .ov-step--3:hover .ov-step__icon { background: rgba(139, 92, 246, .2); }
  .ov-step--4:hover .ov-step__icon,
  .ov-step--5:hover .ov-step__icon,
  .ov-step--6:hover .ov-step__icon { background: rgba(255, 255, 255, .3); }
}

/* The nodes breathe, slowly. They are the only thing on this section that
   moves on its own, and they mark where the climb changes gear. */
.ov-arc__line.is-visible .ov-arc__node--end {
  animation: ovFadeIn .6s ease-out 1.6s both, ovNodePulse 3.4s ease-in-out 2.4s infinite;
}
@keyframes ovNodePulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(255, 255, 255, .9), 0 0 0 0 rgba(255, 77, 54, .5); }
  60%      { box-shadow: 0 0 0 4px rgba(255, 255, 255, .9), 0 0 0 14px rgba(255, 77, 54, 0); }
}

/* --------------------------------------------------------------------------
   The four claims
   -------------------------------------------------------------------------- */

.ov-proof__badge {
  transition: transform .4s cubic-bezier(.2, .7, .2, 1), background-color .3s ease;
}
.ov-proof__claim { transition: color .3s ease; }

@media (hover: hover) and (pointer: fine) {
  .ov-proof__item:hover .ov-proof__badge {
    transform: scale(1.08) rotate(-5deg);
    background: rgba(221, 214, 254, .95);
  }
  .ov-proof__item:hover .ov-proof__claim { color: var(--ov-violet); }
}

/* --------------------------------------------------------------------------
   Service cards: the picture answers the pointer
   -------------------------------------------------------------------------- */

.ov-svc__art { transition: transform .6s cubic-bezier(.2, .7, .2, 1); }

@media (hover: hover) and (pointer: fine) {
  /* Toward the reader, slightly, as if the card had depth. The illustrations
     bleed off the edge, so there is room for this without revealing a seam. */
  .ov-svc:hover .ov-svc__art { transform: scale(1.05) translate(-2%, -1%); }
  .ov-svc--flag:hover .ov-svc__eye { transform: translate(0, -50%) scale(1.06); }
}
.ov-svc__eye { transition: transform .7s cubic-bezier(.2, .7, .2, 1); }

/* --------------------------------------------------------------------------
   Footer links
   -------------------------------------------------------------------------- */

.ov-footer__col a,
.ov-footer__legal a {
  position: relative;
  transition: color .25s ease;
}
.ov-footer__col a::after,
.ov-footer__legal a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .35s cubic-bezier(.2, .7, .2, 1);
}
@media (hover: hover) and (pointer: fine) {
  .ov-footer__col a:hover,
  .ov-footer__legal a:hover { color: #fff; }
  .ov-footer__col a:hover::after,
  .ov-footer__legal a:hover::after { transform: scaleX(1); }
}

/* --------------------------------------------------------------------------
   Pressed states, and the keyboard
   --------------------------------------------------------------------------
   A button that does not move when clicked feels broken on a touch screen,
   where there is no hover to confirm the press. */

.ov-btn { transition: transform .18s ease, box-shadow .3s ease, background-color .3s ease; }
.ov-btn:active { transform: translateY(1px) scale(.99); }

.ov-svc__go:focus-visible,
.ov-footer__col a:focus-visible,
.ov-footer__legal a:focus-visible {
  outline: 2px solid var(--ov-violet-pale);
  outline-offset: 3px;
  border-radius: 4px;
}
.ov-svc:focus-within { transform: translateY(-4px); }

@media (prefers-reduced-motion: reduce) {
  .ov-step, .ov-step__icon, .ov-proof__badge, .ov-svc__art, .ov-svc__eye,
  .ov-footer__col a::after, .ov-footer__legal a::after, .ov-btn { transition: none; }
  .ov-arc__line.is-visible .ov-arc__node--end { animation: none; }
}

/* ==========================================================================
   25. Entrances, both ways
   ==========================================================================
   Stronger than they were, and no longer one-way. The old travel was 18px and
   a fade, which at reading speed is a thing you notice only if you are looking
   for it — and it happened once. */

.ov-rise,
.ov-reveal {
  opacity: 0;
  transform: translate3d(0, 46px, 0) scale(.97);
  transition:
    opacity .7s cubic-bezier(.2, .7, .2, 1),
    transform .7s cubic-bezier(.2, .7, .2, 1);
  will-change: transform, opacity;
}

/* Something that left over the top comes back from the top. Anything else
   rises. The page then reads the same going up as going down, rather than
   everything always arriving from below whichever way you are travelling. */
.ov-rise.from-above,
.ov-reveal.from-above { transform: translate3d(0, -46px, 0) scale(.97); }

.ov-rise.is-visible,
.ov-reveal.is-visible { opacity: 1; transform: none; }

/* The cards in a row arrive one after another rather than all at once. Six
   steps at 70ms is under half a second end to end — a sequence, not a wait. */
.ov-arc__steps .ov-step:nth-child(1) { transition-delay: 0ms; }
.ov-arc__steps .ov-step:nth-child(2) { transition-delay: 70ms; }
.ov-arc__steps .ov-step:nth-child(3) { transition-delay: 140ms; }
.ov-arc__steps .ov-step:nth-child(4) { transition-delay: 210ms; }
.ov-arc__steps .ov-step:nth-child(5) { transition-delay: 280ms; }
.ov-arc__steps .ov-step:nth-child(6) { transition-delay: 350ms; }

.ov-proof__item:nth-child(2) { transition-delay: 80ms; }
.ov-proof__item:nth-child(3) { transition-delay: 160ms; }
.ov-proof__item:nth-child(4) { transition-delay: 240ms; }

/* The bento arrives corner first. */
.ov-svc--02    { transition-delay: 60ms; }
.ov-svc--coral { transition-delay: 120ms; }
.ov-svc--lilac { transition-delay: 180ms; }
.ov-svc--05    { transition-delay: 240ms; }
.ov-svc--ink   { transition-delay: 300ms; }
.ov-svc--07    { transition-delay: 360ms; }

/* --------------------------------------------------------------------------
   Driven by the scroll itself
   --------------------------------------------------------------------------
   Where the browser supports it, the process line is not a timed animation
   that plays once when it appears — it is drawn by the scroll position, so it
   advances as you go down and retreats as you come back up. That is the one
   thing on the page that genuinely follows the wheel rather than being
   triggered by it.

   Everything below is inside @supports because browsers without it keep the
   timed version, which is what the rules above already give them. */

/* The line is drawn by the scroll position, in drawOnScroll() — see main.js.

   CSS scroll-driven timelines were the obvious way to do this and are what
   was written first. They resolve here: the browser reports a ViewTimeline and
   accepts the range. What could not be shown is that the timeline advances —
   the progress read the same at every scroll position on the page, whether the
   subject was the SVG path (which has no layout box, so that one was
   explicable) or the div around it (which does). Rather than ship an effect
   that cannot be demonstrated to work, it is driven from the scroll handler,
   where the value can be read back and checked. */

@media (prefers-reduced-motion: reduce) {
  .ov-rise,
  .ov-reveal {
    opacity: 1;
    transform: none;
    transition: none;
    will-change: auto;
  }
}

/* ==========================================================================
   26. Selected results — the process section's furniture, on the dark band
   ==========================================================================
   Same geometry, same rhythm, same behaviour: a number, an icon in a tinted
   square, the figure, the line that explains it. The colours are the ones
   thing that cannot be carried across — the staircase climbs a pale ground
   and this is the page's only dark band, so the ramp runs glass, violet,
   coral instead of white upward. */

.ov-case {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 300px;
  padding: clamp(18px, 1.7vw, 26px);
  border-radius: clamp(16px, 1.5vw, 22px);
  overflow: hidden;
  isolation: isolate;
  background: rgba(255, 255, 255, .05);
  border: 1px solid rgba(255, 255, 255, .09);
  transition:
    transform .45s cubic-bezier(.2, .7, .2, 1),
    box-shadow .45s ease;
}

.ov-case__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  /* A fixed height because only two of the three cards carry a duration, and
     without it the one that does not sat six pixels higher than its
     neighbours all the way down. */
  min-height: 18px;
}
.ov-case__n {
  font-family: var(--ov-font-display);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .45);
}
.ov-case__meta {
  font-size: 12px;
  font-weight: 600;
  color: var(--ov-coral);
}

.ov-case__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 14px;
  color: #fff;
  background: rgba(255, 255, 255, .12);
  transition:
    transform .45s cubic-bezier(.2, .7, .2, 1),
    background-color .35s ease;
}
.ov-case__icon .ov-icon { width: 21px; height: 21px; }

/* The body takes the slack, so all three figures sit on one line however long
   the descriptions run. */
.ov-case__body { display: flex; flex-direction: column; flex: 1 1 auto; justify-content: flex-end; }

.ov-case__sector {
  margin-bottom: 10px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .5);
}
.ov-case__figure {
  margin: 0 0 8px;
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: clamp(34px, 3.6vw, 50px);
  /* The middle figure sits six pixels higher than its neighbours, and that is
     the design rather than a fault: .ov-case--feature sets it larger on
     purpose, because it is the card the section is built around. Chased as a
     bug first — the theory was a font fallback on U+2212 — and measured back
     to a deliberate rule. */
  line-height: 1;
  letter-spacing: -.045em;
  color: #fff;
}
.ov-case__figure--accent { color: var(--ov-coral); }
.ov-case__desc {
  margin: 0;
  /* Two lines' worth whether it needs them or not, so the three figures sit on
     one line. Bottom-aligned bodies alone were not enough: a one-line
     description pushed its figure down to where the two-line ones ended. */
  min-height: calc(1.5em * 2);
  font-size: 13.5px;
  line-height: 1.5;
  color: rgba(255, 255, 255, .7);
}

/* The ramp. Glass, then the violet the middle card already had, then coral —
   the same warming the staircase does, read left to right instead of upward. */
.ov-case--feature {
  background: linear-gradient(150deg, #7c5cf5, #5b31d6);
  border-color: transparent;
  box-shadow: 0 40px 80px -46px rgba(91, 49, 214, .9);
}
.ov-case--feature .ov-case__n     { color: rgba(255, 255, 255, .7); }
.ov-case--feature .ov-case__icon  { background: rgba(255, 255, 255, .2); }
.ov-case--feature .ov-case__desc  { color: rgba(255, 255, 255, .82); }

.ov-case--accent { border-color: rgba(255, 122, 107, .3); }
.ov-case--accent .ov-case__icon { color: var(--ov-coral); background: rgba(255, 122, 107, .16); }

/* The same answer to the pointer the staircase gives. */
@media (hover: hover) and (pointer: fine) {
  .ov-case:hover {
    transform: translateY(-6px);
    box-shadow: 0 34px 64px -34px rgba(0, 0, 0, .6);
  }
  .ov-case:hover .ov-case__icon {
    transform: translate(3px, -3px) rotate(-4deg);
    background: rgba(255, 255, 255, .22);
  }
  .ov-case--accent:hover .ov-case__icon { background: rgba(255, 122, 107, .26); }
  .ov-case--feature:hover { box-shadow: 0 44px 84px -40px rgba(91, 49, 214, 1); }
}

/* And they arrive in sequence, like the steps do. */
.ov-cases .ov-case:nth-child(2) { transition-delay: 90ms; }
.ov-cases .ov-case:nth-child(3) { transition-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
  .ov-case, .ov-case__icon { transition: none; }
}

/* The lip out of the dark results band.

   Pinned to the foot of the section and transparent, so the dark ground runs
   on behind it and only the shape below the curve is painted pale. Without it
   the band ended on a straight horizontal cut across the full width — the same
   thing the footer's wave was fixing at the other end of the page. */
.ov-travel { position: relative; padding-top: 150px; }

.ov-curve--work {
  position: absolute;
  left: 0;
  right: 0;
  /* One pixel up, one pixel taller. At the section boundary the browser's
     rounding left a single row that neither the band above nor this fill
     painted, and the page's pale ground showed through it as a hairline across
     the full width — measured at (235,230,251) between two rows of (29,18,55). */
  top: -1px;
  height: 105px;
  background: transparent;
  z-index: 2;
}

/* Flat ink, and it matches: the results band carries a violet glow, but that
   glow is anchored to its top, so by the foot of the section the ground is
   the base colour and nothing else. */
.ov-curve--work .ov-curve__fill { fill: var(--ov-ink); }

/* ---------------------------------------------------------------------------
   Travel — a different temperament, the same palette.

   Not one colour token is redefined here. Violet is still the brand, coral is
   still the accent, the lavender ground is unchanged, and so are the three
   typefaces. What changes is the handling: the front page sells a service and
   is built tight and crisp for it, while this page sells a place to go and can
   afford to breathe.

   So the corners round off, the sections open up, the glass thins and the blur
   deepens, and the motion settles rather than snaps. Same brand, different
   register — which is the difference between a page that looks like a
   different section and one that looks like a different website.
   --------------------------------------------------------------------------- */
body.page-template-page-travel {
	/* Rounder. The marketing page's 13-26px reads as considered and businesslike;
	   at these radii the same cards read as relaxed. */
	--ov-r-sm:  18px;
	--ov-r-md:  24px;
	--ov-r-lg:  30px;
	--ov-r-xl:  36px;
	--ov-r-2xl: 40px;

	/* More air between sections, and a slightly wider column, so photography
	   has room instead of being packed the way a services grid wants to be. */
	--ov-section-y: clamp(60px, 8vw, 124px);
	--ov-gutter:    30px;
	--ov-max:       1240px;

	/* Thinner glass over a deeper blur: the panels sit in the page rather than
	   on top of it, which is what makes a travel page feel atmospheric. */
	--ov-glass:        rgba(255, 255, 255, .46);
	--ov-glass-soft:   rgba(255, 255, 255, .38);
	--ov-glass-strong: rgba(255, 255, 255, .62);
	--ov-blur:         blur(30px);

	/* A longer settle on the same curve family — drift, not snap. */
	--ov-ease: cubic-bezier(.16, .84, .28, 1);
}

/* Travel page header photograph.
   Laid in behind the copy and faded out to the page's own ground before it
   reaches the text, the same way the front page's hero handles its picture:
   the words sit on paper and the photograph is what you look past them into.
   The bottom edge fades too, so the section below starts on flat colour
   instead of a cut. */
.ov-pagehead--photo { position: relative; isolation: isolate; overflow: hidden; }

.ov-pagehead--photo .ov-pagehead__photo { position: absolute; inset: 0; z-index: -1; }

.ov-pagehead--photo .ov-pagehead__photo img {
	width: 100%; height: 100%;
	object-fit: cover;
	object-position: 62% 40%;
}

.ov-pagehead--photo .ov-pagehead__veil {
	position: absolute; inset: 0;
	background:
		linear-gradient(100deg, var(--ov-bg) 0%, var(--ov-bg) 34%, transparent 74%),
		linear-gradient(to bottom, transparent 48%, var(--ov-bg) 100%);
}

/* The auras were doing the lifting on a flat ground; over a photograph they
   only muddy it. */
.ov-pagehead--photo .ov-pagehead__aura { opacity: .32; }

/* On a phone the copy runs the full width, so the veil has to cover it. */
@media (max-width: 780px) {
	.ov-pagehead--photo .ov-pagehead__veil {
		background:
			linear-gradient(to bottom, transparent 0%, var(--ov-bg) 62%),
			linear-gradient(to right, var(--ov-bg) 0%, transparent 100%);
	}
	.ov-pagehead--photo .ov-pagehead__photo img { object-position: 58% 34%; }
}

.ov-faq__more { margin-top: 24px; }


/* ---------------------------------------------------------------------------
   Travel hero — recovered.

   The original Travel landing opened with a full hero: the photograph bled to
   the edges, the headline sat over it and two cards floated on the picture.
   Rebuilding the front page's hero for marketing deleted the rules it needed
   (.ov-hero__media, .ov-hero__stage, .ov-float and friends, .ov-play), so the
   page had been left with a plain header ever since.

   These are those rules, recovered from the initial commit and scoped to
   .ov-hero--travel so the marketing hero keeps its own. The keyframes they
   animate on, ov-float and ov-kenburns, are still in the file above.
   --------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   9. Hero
   -------------------------------------------------------------------------- */
.ov-hero--travel {
  position: relative;
  margin-top: -88px;
}
.ov-hero--travel .ov-hero__stage {
  position: relative;
  min-height: clamp(540px, 78vh, 760px);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}
.ov-hero--travel .ov-hero__media {
  position: absolute;
  inset: -8% 0 0;
  /* Eased fade with a long, low-alpha tail. A linear ramp reads as ending
     abruptly; concentrating stops near the end lets the image approach zero
     asymptotically, so the eye never finds an edge. */
  -webkit-mask-image: linear-gradient(180deg,
    #000 0%, #000 44%,
    rgba(0,0,0,.96) 54%, rgba(0,0,0,.88) 62%,
    rgba(0,0,0,.74) 69%, rgba(0,0,0,.56) 76%,
    rgba(0,0,0,.38) 82%, rgba(0,0,0,.24) 88%,
    rgba(0,0,0,.13) 93%, rgba(0,0,0,.05) 97%, transparent 100%);
  mask-image: linear-gradient(180deg,
    #000 0%, #000 44%,
    rgba(0,0,0,.96) 54%, rgba(0,0,0,.88) 62%,
    rgba(0,0,0,.74) 69%, rgba(0,0,0,.56) 76%,
    rgba(0,0,0,.38) 82%, rgba(0,0,0,.24) 88%,
    rgba(0,0,0,.13) 93%, rgba(0,0,0,.05) 97%, transparent 100%);
}
.ov-hero--travel .ov-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ov-hero--travel .ov-hero__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(268deg,
      rgba(240,249,255,.78) 0%,
      rgba(240,249,255,.72) 24%,
      rgba(238,248,255,.46) 48%,
      rgba(235,247,255,.14) 74%,
      rgba(235,247,255,.04) 100%),
    linear-gradient(180deg,
      rgba(255,255,255,.22) 0%,
      rgba(255,255,255,0) 18%,
      rgba(255,255,255,0) 52%,
      rgba(255,255,255,.10) 76%,
      rgba(255,255,255,.06) 92%,
      rgba(255,255,255,0) 100%);
  /* The veil has to fade out too. It used to end on a hardcoded #d3e9fa, which
     met the page gradient at a different colour and drew a hard seam across the
     bottom of the hero. Masking it out lets the page gradient carry through. */
  -webkit-mask-image: linear-gradient(180deg,
    #000 0%, #000 50%, rgba(0,0,0,.82) 66%, rgba(0,0,0,.52) 78%,
    rgba(0,0,0,.26) 88%, rgba(0,0,0,.08) 95%, transparent 100%);
  mask-image: linear-gradient(180deg,
    #000 0%, #000 50%, rgba(0,0,0,.82) 66%, rgba(0,0,0,.52) 78%,
    rgba(0,0,0,.26) 88%, rgba(0,0,0,.08) 95%, transparent 100%);
}
.ov-hero--travel .ov-hero__inner {
  position: relative;
  width: 100%;
  max-width: var(--ov-max-wide);
  margin-inline: auto;
  padding: clamp(96px, 9vw, 116px) var(--ov-gutter) clamp(58px, 6vw, 84px);
  display: flex;
}
.ov-hero--travel .ov-hero__copy {
  position: relative;
  order: 2;
  max-width: 520px;
  margin-left: auto;
  margin-right: clamp(0px, 7vw, 110px);
}
.ov-hero--travel .ov-hero__copy .ov-pill { margin-bottom: 16px;
}
.ov-hero--travel .ov-hero__copy .ov-h1 { margin-bottom: 14px;
}
.ov-hero--travel .ov-hero__sub {
  font-size: clamp(14.5px, 1.4vw, 16.5px);
  line-height: 1.6;
  color: var(--ov-lead);
  margin-bottom: 24px;
}
.ov-hero--travel .ov-hero__cta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(184px, 1fr));
  gap: 12px;
  align-items: stretch;
}
/* Floating cards to the left of the hero */
.ov-hero--travel .ov-hero__floats {
  order: 1;
  align-self: flex-end;
  margin: 0 auto 6px 0;
  display: grid;
  gap: 12px;
  justify-items: start;
}
.ov-hero--travel .ov-float {
  border-radius: var(--ov-r-md);
  overflow: hidden;
  box-shadow: 0 18px 38px rgba(9, 44, 80, .2);
  animation: ov-float 9s ease-in-out infinite;
}
.ov-hero--travel .ov-float:first-child {
  border-radius: 20px;
  box-shadow: 0 20px 44px rgba(9, 44, 80, .24);
  animation-duration: 8s;
}
.ov-hero--travel .ov-float__body {
  background: rgba(255, 255, 255, .62);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
  border: 1px solid var(--ov-glass-brd);
  border-radius: inherit;
  padding: 14px 18px;
}
.ov-hero--travel .ov-float__big {
  font-family: var(--ov-font-display);
  font-weight: 800;
  font-size: 23px;
  line-height: 1;
  color: var(--ov-violet-deep);
}
.ov-hero--travel .ov-float__label {
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ov-teal-dark);
}
.ov-hero--travel .ov-float__note { margin-top: 4px; font-size: 12.5px; color: var(--ov-body);
}

/* Travel hero — the three corrections.

   1. The cards belong to the flex row, not to the stage. Rebuilding the front
      page's hero pinned its own cards with position:absolute on the shared
      .ov-hero__floats class, which left these two stacked in a corner with no
      offsets of their own. This puts them back in the flow they were designed
      in.
   2. Raised off the foot of the picture, so they float on the water rather
      than sitting on the wave that now cuts the bottom of it.
   3. The mask that used to fade the photograph out at the bottom is gone: the
      curve does the cutting now, and a fade under it would only wash it out.
      The stage takes the wave's height as padding so nothing is covered. */
.ov-hero--travel .ov-hero__floats { position: relative; inset: auto; }
.ov-hero--travel .ov-hero__floats > * { position: static; }
.ov-hero--travel .ov-hero__floats { margin-bottom: clamp(52px, 9vh, 112px); }

.ov-hero--travel .ov-hero__media {
	-webkit-mask-image: none;
	mask-image: none;
}

/* Just enough room under the copy that the wave does not cut into it. The
   search card climbs back over this on its own, with the margin the theme
   already gives it. */
.ov-hero--travel .ov-hero__inner { padding-bottom: clamp(116px, 12vw, 148px); }

@media (max-width: 900px) {
	.ov-hero--travel .ov-hero__floats { margin-bottom: 0; }
}

/* The wave that cuts the Travel hero fills with the page's own ground.

   The front page's copy of this wave is near-white on purpose: what sits under
   the hero there is the white discipline rail, and the fill is matched to it.
   The Travel page has no such band — under its hero is the page itself, so
   filling white painted a white stripe across the photograph instead of
   ending it on a curve. Filled with the ground, the picture reads as cut to
   the wave and the page carries on beneath it. */
.ov-hero--travel .ov-curve--hero .ov-curve__fill { fill: var(--ov-bg); }

/* The rule my recovery missed, and the reason the photograph ended on a
   straight cut above the wave instead of on it.

   Without it the <img> keeps its intrinsic aspect, so it stops wherever its
   own height runs out and leaves the foot of the media box empty — the page
   showing through in a hard horizontal line, with the curve drawn uselessly
   below it. Filling the box is what lets the wave do the cutting. */
.ov-hero--travel .ov-hero__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% 42%;
}

/* The photograph is cut to the wave itself, not covered by one.

   Painting the wave's shape over the picture only works where the fill can
   match whatever sits behind it. Under this hero is the page's dotted band,
   and a flat fill over that reads as a wedge of colour laid on the photo
   rather than as the photo ending. So the image carries the curve in its own
   mask and everything behind it — dots, gradient, whatever the section below
   brings — shows through the cut.

   Two mask layers, unioned: a solid one for everything above the last 100px,
   and the wave for those 100px. The path is the same one ov_divider() draws
   for the 'hero' wave, so the cut and the drawn line agree exactly. */
.ov-hero--travel .ov-hero__media {
	--ov-wave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 100' preserveAspectRatio='none'%3E%3Cpath fill='%23000' d='M0 0 H1440 V34 C 1180 6 1040 74 760 62 C 520 52 360 96 0 58 Z'/%3E%3C/svg%3E");

	-webkit-mask-image: linear-gradient(#000, #000), var(--ov-wave);
	        mask-image: linear-gradient(#000, #000), var(--ov-wave);
	-webkit-mask-size: 100% calc(100% - 100px), 100% 100px;
	        mask-size: 100% calc(100% - 100px), 100% 100px;
	-webkit-mask-position: top left, bottom left;
	        mask-position: top left, bottom left;
	-webkit-mask-repeat: no-repeat, no-repeat;
	        mask-repeat: no-repeat, no-repeat;
}

/* With the picture cut, the wave's own fill would only paint over the cut it
   is meant to reveal. The drawn line and its dot stay. */
.ov-hero--travel .ov-curve--hero .ov-curve__fill { fill: transparent; }

/* The search card, centred on the foot of the photograph.

   The theme lifts it between 115 and 185px, which was measured against a hero
   that faded out softly and ended higher than this one does. With the picture
   now cut by the wave, that leaves the card sitting mostly below the image.
   Lifting it to roughly half its own height above the cut puts its middle on
   the image's edge: half on the photograph, half on the page.

   Nothing here touches the glass. The card keeps the theme's own transparency,
   whose label colours were contrast-measured against what shows through it. */
body.page-template-page-travel .ov-searchband {
	margin-top: clamp(-320px, -24vh, -240px);
}

/* A little more milk in the glass.

   The card's fill is the body's own 20% white over its heavy blur — the tint
   behind it is switched off, which is why raising that had no effect. This
   lifts the white and leaves everything else the theme decided alone: the
   blur, the darkened label colours, the opaque fields. More white can only
   help the contrast those were measured for, never hurt it. */
body.page-template-page-travel .ov-searchbox__body {
	background: rgba(255, 255, 255, .86);
	/* Deeper than the theme's 40px: with this much white in front of it the
	   blur has less to do, so it takes more of it to still read as depth
	   rather than as flat paint. The saturation is what keeps the water's
	   turquoise alive through all that milk. */
	-webkit-backdrop-filter: blur(44px) saturate(1.7);
	        backdrop-filter: blur(44px) saturate(1.7);
}

/* A violet wash down the top of the Travel hero photograph.

   Laid on the media rather than over the whole section, so it is cut by the
   same wave mask the picture is and never spills past the curve. It sits
   under the veil that lightens the copy side, so the headline keeps its
   ground and only the sky takes the colour.

   The violet is the brand's own 91,49,214 — the same one the drawn rules and
   the buttons use — fading out well before the horizon so the water stays
   the colour it actually is. */
.ov-hero--travel .ov-hero__media::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	/* It starts on the page's own ground rather than on violet, so the top of
	   the photograph dissolves into the page instead of beginning on a ruled
	   line. The violet is the step between the two: opaque background at the
	   very edge, brand colour through the sky, gone before the horizon. */
	background: linear-gradient(
		180deg,
		var(--ov-bg) 0%,
		var(--ov-bg) 3%,
		rgba(139, 92, 246, .62) 9%,
		rgba(91, 49, 214, .40) 17%,
		rgba(91, 49, 214, .22) 29%,
		rgba(91, 49, 214, .08) 42%,
		transparent 56%
	);
}

/* The photograph starts where the section does.

   Inherited from the original hero, the media box hung 8% above the stage —
   room for the Ken Burns drift to travel into. But every percentage in the
   gradient over it was then measured against a box whose top 8% is clipped
   away, so the run of page colour meant to hide the picture's top edge fell
   entirely inside the hidden part and the cut stayed visible. Aligned to the
   stage, the stops mean what they say. The drift still has somewhere to go:
   ov-kenburns scales the image, it does not need slack above it. */
.ov-hero--travel .ov-hero__media { inset: 0; }

/* And the fade recalculated against that box: solid page colour across the
   top edge, easing through the brand violet, gone before the horizon. Enough
   stops that the eye cannot find a step to call an edge. */
.ov-hero--travel .ov-hero__media::after {
	background: linear-gradient(
		180deg,
		var(--ov-bg) 0%,
		var(--ov-bg) 4%,
		rgba(221, 214, 254, .92) 7%,
		rgba(167, 139, 250, .74) 11%,
		rgba(139, 92, 246, .52) 16%,
		rgba(91, 49, 214, .34) 23%,
		rgba(91, 49, 214, .20) 32%,
		rgba(91, 49, 214, .10) 42%,
		rgba(91, 49, 214, .03) 52%,
		transparent 62%
	);
}

/* One transition at the top, not two.

   Painting a run of page colour over the picture and then a band of violet
   under it drew two edges: where the flat colour ended and where the violet
   met the photograph. Neither was the picture ending — both were shapes laid
   on top of it.

   So the image itself fades out instead. The mask's solid layer now starts
   transparent and comes in over the first sixth, which lets the page show
   through the photograph rather than covering it, and there is nothing left
   to draw an edge. The wave at the foot is unchanged. */
.ov-hero--travel .ov-hero__media {
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .45) 6%, #000 17%), var(--ov-wave);
	        mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .45) 6%, #000 17%), var(--ov-wave);
}

/* And the violet becomes a tint on the sky rather than a band across it: no
   opaque stop anywhere, so it can only colour what the fade already left. */
.ov-hero--travel .ov-hero__media::after {
	background: linear-gradient(
		180deg,
		rgba(139, 92, 246, .40) 0%,
		rgba(110, 68, 224, .30) 12%,
		rgba(91, 49, 214, .19) 24%,
		rgba(91, 49, 214, .09) 36%,
		rgba(91, 49, 214, .03) 48%,
		transparent 60%
	);
}

/* No fade at the top: the photograph starts at full strength from the very
   edge. Only the wave at the foot is masked now — the solid layer covers
   everything above it. */
.ov-hero--travel .ov-hero__media {
	-webkit-mask-image: linear-gradient(#000, #000), var(--ov-wave);
	        mask-image: linear-gradient(#000, #000), var(--ov-wave);
}

/* The strip of page showing above the photograph.

   This hero inherited a -88px pull from the design it came from, when the
   header was a different height. The header is now up to 90px tall — its own
   padding plus the bar — so at the top of that range the hero stopped short
   and left a few pixels of page across the full width, read as a ruled line
   above the picture. The front page's hero already solved this: the theme
   settled on -110px precisely because it clears the header at every value the
   padding can take. Same number here, same reason. */
.ov-hero--travel { margin-top: -110px; }

/* Lift the hero copy off the photograph.

   Two shadows rather than one: a tight, faint pass that gives every letter an
   edge against whatever detail sits behind it, and a wide, soft pass that
   reads as the text floating above the picture rather than printed on it. A
   single hard shadow at this size looks stamped on.

   Kept in the ocean ink the page already uses rather than black, which over a
   sunset would go grey and dirty the colour underneath. */
.ov-hero--travel .ov-hero__copy .ov-h1 {
	text-shadow:
		0 1px 2px rgba(9, 28, 52, .26),
		0 10px 34px rgba(9, 28, 52, .34);
}

.ov-hero--travel .ov-hero__sub {
	text-shadow:
		0 1px 2px rgba(9, 28, 52, .22),
		0 6px 22px rgba(9, 28, 52, .26);
}

/* The pill and the buttons carry their own backgrounds, so they only need the
   tight pass to keep their edges crisp over busy water. */
.ov-hero--travel .ov-hero__copy .ov-pill {
	box-shadow: 0 6px 20px rgba(9, 28, 52, .22);
}

/* The washed section background, which was only ever a comment.

   The stories section describes its background as "a photograph washed almost
   to nothing", but nothing in the stylesheet ever washed it: .ov-secbg sets a
   faint violet ground and a loading shimmer, and the image on top of it paints
   at full strength. The branch had simply never run, because no photograph had
   ever been assigned to that slot.

   Only the 'wash' variant is treated. The tours and savings backgrounds use
   'plain' and are meant to be seen. */
.ov-secbg--wash img {
	opacity: .16;
	filter: saturate(.7);
}

/* .ov-secbg as the background layer it was always meant to be.

   The section scaffolding says so plainly: .ov-section--bg is position:
   relative and lifts its .ov-wrap to z-index 1, which is only worth doing if
   something sits behind it. But .ov-secbg itself stayed position: relative and
   in the flow, so instead of covering the section it took a block of height at
   the top of it, at the image's own aspect ratio, with hard edges down the
   right and across the bottom.

   Like the wash, this had never shown: every section using it had an empty
   slot, so the markup never rendered and the defect never surfaced. */
.ov-secbg {
	position: absolute;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	pointer-events: none;
}

.ov-secbg img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* The whole layer fades, not just the photograph inside it.

   The mask was on the image, so the picture dissolved at the section's edges
   but the layer's own violet tint did not — and a flat tint with nothing
   softening it draws a ruled line straight across the page wherever the
   section starts and stops. Masking the container takes the tint and the
   photograph out together, and the join between sections stops existing. */
.ov-secbg--wash {
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 76%, transparent 100%);
	        mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 76%, transparent 100%);
}

/* Every section background loses its edges, not just the washed one.

   The savings and tours backgrounds use the 'plain' variant and had no fade
   at all, so each one began and ended on a ruled line across the full width —
   the join between sections, drawn in. This puts a short fade on the base
   class so it applies to all of them.

   Short on purpose: these two are meant to be looked at, unlike the wash, so
   the fade only has to cover the join. The wash keeps its own longer one,
   declared after this. */
.ov-secbg {
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 9%, #000 91%, transparent 100%);
	        mask-image: linear-gradient(180deg, transparent 0%, #000 9%, #000 91%, transparent 100%);
}

.ov-secbg--wash {
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 76%, transparent 100%);
	        mask-image: linear-gradient(180deg, transparent 0%, #000 22%, #000 76%, transparent 100%);
}

/* The curved variant, which the template has been asking for all along.

   ov_section_bg() takes a $curved flag and the tours section passes it, so the
   markup has carried .ov-secbg--curved from the start — but nothing in the
   stylesheet ever answered to it, and the background ended on the same
   straight fade as the rest.

   Same wave the hero is cut with, so the two curves on this page are one
   shape rather than two similar ones. The top keeps a short fade; only the
   foot becomes a curve. */
.ov-secbg--curved {
	--ov-wave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 100' preserveAspectRatio='none'%3E%3Cpath fill='%23000' d='M0 0 H1440 V34 C 1180 6 1040 74 760 62 C 520 52 360 96 0 58 Z'/%3E%3C/svg%3E");

	-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 11%), var(--ov-wave);
	        mask-image: linear-gradient(180deg, transparent 0%, #000 11%), var(--ov-wave);
	-webkit-mask-size: 100% calc(100% - 112px), 100% 112px;
	        mask-size: 100% calc(100% - 112px), 100% 112px;
	-webkit-mask-position: top left, bottom left;
	        mask-position: top left, bottom left;
	-webkit-mask-repeat: no-repeat, no-repeat;
	        mask-repeat: no-repeat, no-repeat;
}

/* The two photographic backgrounds fade in opposite directions.

   Savings — the clouds — holds solid across the top and lets go from halfway
   down, so the sky is at full strength where the section begins and has
   dissolved by the time the cards below it need a clean ground.

   Tours — the balloons — does the reverse: it arrives out of nothing at the
   top, which is what makes the balloons look like they drifted into the
   section rather than being pasted across it, and keeps its wave at the foot.

   Told apart by the curve flag: only tours passes it. */
.ov-secbg--plain:not(.ov-secbg--curved) {
	/* In as gradually as it goes out. Coming solid at 7% was near enough a
	   step: the sky arrived almost at once and the entry read as an edge,
	   which is the thing all of this is for. Now it builds over the first
	   third and lets go from a little past halfway. */
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .28) 12%, rgba(0, 0, 0, .68) 24%, #000 36%, #000 52%, transparent 100%);
	        mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .28) 12%, rgba(0, 0, 0, .68) 24%, #000 36%, #000 52%, transparent 100%);
}

.ov-secbg--curved {
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .3) 16%, rgba(0, 0, 0, .72) 30%, #000 46%), var(--ov-wave);
	        mask-image: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .3) 16%, rgba(0, 0, 0, .72) 30%, #000 46%), var(--ov-wave);
}


/* The closing panel with nothing beside it.

   The join panel was built as two columns — copy on one side, the quote form
   on the other. With the form gone there is only the copy, and left in the
   grid it sat in half the panel with the other half empty. */
.ov-join__body--single {
	display: block;
	max-width: 46rem;
	margin-inline: auto;
	text-align: center;
}

.ov-join__body--single .ov-h2 { margin: 10px 0 14px; }
.ov-join__body--single .ov-lede { margin: 0 auto 26px; }
.ov-join__go { margin: 0 0 22px; }
.ov-join__body--single .ov-join__contact { justify-content: center; }

/* The savings grid, evened out.

   It was a bento: hotels and resort at span 8, cruises at 5, the rest at 3
   and 4, with the section heading taking a column beside them. That works
   when the cards carry figures of different weight — one headline number, the
   others supporting it. With the numbers gone they all say the same kind of
   thing, and the uneven spans just left a hole down the right. Six equal
   cards, three to a row, heading above them. */
body.page-template-page-travel .ov-bento__head { grid-column: span 12; padding-right: 0; }

.ov-bcard--hotels,
.ov-bcard--cruises,
.ov-bcard--flights,
.ov-bcard--cars,
.ov-bcard--activities,
.ov-bcard--resort {
	grid-column: span 4;
	min-height: 260px;
}

/* Evening them out has to survive the narrow screens too. These six spans sit
   at the end of the sheet, after the breakpoints that used to collapse the old
   uneven bento, so a flat `span 4` here quietly outranked them and the grid
   stayed three across on a phone — cards about a hundred pixels wide, with the
   copy breaking to a word a line. The same two breakpoints, restated. */
@media (max-width: 900px) {
	.ov-bcard--hotels,
	.ov-bcard--cruises,
	.ov-bcard--flights,
	.ov-bcard--cars,
	.ov-bcard--activities,
	.ov-bcard--resort { grid-column: span 6; }
}
@media (max-width: 560px) {
	.ov-bcard--hotels,
	.ov-bcard--cruises,
	.ov-bcard--flights,
	.ov-bcard--cars,
	.ov-bcard--activities,
	.ov-bcard--resort { grid-column: span 12; }
}

/* The glass panel over photo cards: tried, reverted.

   The photo cards' own device — a dark wash at the foot of the picture with
   the type reversed out in white — was once swapped for the header's glass
   recipe, on the reasoning that the pale panels would stop reading as holes
   punched in the photographs.

   That reasoning was drawn from a broken layout. A shared `position: relative`
   further up had knocked `.ov-tcard__media` and `.ov-bcard__media` out of
   their absolute inset, so the photograph sat *beside* the panel rather than
   under it, and of course an ink panel looked wrong next to a picture instead
   of over one. With the media covering its card again, the original wash is
   the right device: the photograph stays visible, and white on a dark scrim
   holds its contrast without any opacity to tune.

   The originals live with their sections — .ov-bcard--photo .ov-bcard__body,
   .ov-tcard__body and .ov-story-hero__body — and need nothing from here. */
