/* ==========================================================================
   VeaHealth — motion layer
   Styles for the effects driven by assets/js/motion.js: the custom cursor, the
   scroll-scrubbed hero film, the fullscreen menu, the pinned journey track and
   the line-mask headline reveal.

   Everything here is additive. If JavaScript never runs, none of these classes
   are applied and the page renders exactly as it does without the motion layer.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Custom cursor
   Pointer-fine devices only, and never when the visitor asks for less motion.
   -------------------------------------------------------------------------- */
.vh-cursor,
.vh-cursor-dot {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 200;
  pointer-events: none;
  will-change: transform;
  opacity: 0;
  transition: opacity .3s var(--ease);
}

.vh-cursor {
  width: 40px;
  height: 40px;
  margin: -20px 0 0 -20px;
  border: 1px solid color-mix(in srgb, var(--teal) 55%, transparent);
  border-radius: 50%;
  display: grid;
  place-items: center;
  backdrop-filter: invert(0);
}

.vh-cursor-dot {
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: 50%;
  background: var(--teal);
}

.has-cursor .vh-cursor,
.has-cursor .vh-cursor-dot { opacity: 1; }

/* the label that appears inside the ring on media and sliders */
.vh-cursor-label {
  font-family: var(--f-mono);
  font-size: .56rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--deep-ink);
  opacity: 0;
  transform: scale(.6);
  white-space: nowrap;
  transition: opacity .25s var(--ease), transform .25s var(--ease-out);
}

/* states, set from JS by reading data-cursor on the hovered element */
.vh-cursor[data-state="link"]   { border-color: var(--teal); }
.vh-cursor[data-state="media"],
.vh-cursor[data-state="drag"]   {
  background: var(--teal);
  border-color: transparent;
}
.vh-cursor[data-state="media"] .vh-cursor-label,
.vh-cursor[data-state="drag"]  .vh-cursor-label { opacity: 1; transform: none; }
.has-cursor.cursor-hidden .vh-cursor,
.has-cursor.cursor-hidden .vh-cursor-dot { opacity: 0; }

/* hide the native cursor only once ours is actually running */
.has-cursor,
.has-cursor a,
.has-cursor button,
.has-cursor .ba { cursor: none; }
/* …but never over something you type into */
.has-cursor input,
.has-cursor textarea,
.has-cursor select { cursor: auto; }

/* --------------------------------------------------------------------------
   2. Scroll-scrubbed hero film
   The clip does not play on its own: its currentTime is driven by scroll.
   -------------------------------------------------------------------------- */
.hero--film { min-height: 100vh; }

.hero-film {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  background: var(--deep);
}
.hero-film video,
.hero-film canvas,
.hero-film img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/*
 * The <video> is a decoder, not a picture: motion.js copies each frame into the
 * canvas, because a paused seek-only video does not reliably composite. It stays
 * in the DOM and stays invisible.
 */
.hero-film video { opacity: 0 !important; }
.hero-film canvas { opacity: 0; transition: opacity .8s var(--ease); }
.hero-film.is-ready canvas { opacity: 1; }
/* the still holds the frame until the canvas has one to show */
.hero-film img { transition: opacity .8s var(--ease); }
.hero-film.is-ready img { opacity: 0; }

/*
 * The scrim. It has to hold white text legible over every frame of the clip,
 * and the clip runs from a dark waterline to a sunlit sky — so it is weighted
 * across the text column rather than evenly, which keeps the skyline on the
 * right readable instead of flattening the whole picture to protect one corner.
 */
.hero-film::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(6,20,22,.88) 0%, rgba(6,20,22,.78) 30%, rgba(6,20,22,.44) 56%, rgba(6,20,22,.16) 78%, rgba(6,20,22,.10) 100%),
    linear-gradient(180deg, rgba(6,20,22,.42) 0%, rgba(6,20,22,.24) 30%, rgba(6,20,22,.72) 100%);
}
@media (max-width: 860px) {
  /* the copy runs the full width on a phone, so the scrim has to as well */
  .hero-film::after {
    background:
      linear-gradient(180deg, rgba(6,20,22,.62) 0%, rgba(6,20,22,.52) 30%, rgba(6,20,22,.86) 100%);
  }
}

/* a thin progress rail showing how far through the film the scroll has gone */
.hero-scrub {
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: 26px;
  height: 1px;
  background: rgba(255,255,255,.18);
  overflow: hidden;
}
.hero-scrub span {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--teal-bright);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* --------------------------------------------------------------------------
   3. Headline reveal — one mask per line
   -------------------------------------------------------------------------- */
.js .vh-lines { overflow: hidden; }
.js .vh-line { display: block; overflow: hidden; }
.js .vh-line > span {
  display: block;
  transform: translateY(105%);
  will-change: transform;
}

/* --------------------------------------------------------------------------
   4. Fullscreen menu
   -------------------------------------------------------------------------- */
.menu-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  margin-inline-start: 12px;
  padding: 10px 4px;
  font-family: var(--f-mono);
  font-size: .68rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink);
}
.menu-toggle .bars { display: block; width: 22px; }
.menu-toggle .bars i {
  display: block;
  height: 1.4px;
  background: currentColor;
  transition: transform .45s var(--ease-out), opacity .3s;
}
.menu-toggle .bars i + i { margin-top: 5px; }
.menu-toggle[aria-expanded="true"] .bars i:nth-child(1) { transform: translateY(6.4px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] .bars i:nth-child(2) { opacity: 0; }
.menu-toggle[aria-expanded="true"] .bars i:nth-child(3) { transform: translateY(-6.4px) rotate(-45deg); }

/* While the overlay is up the header rides above it, so the X that closes the
   menu is on top of the menu rather than beneath it, and reads against the
   dark ground it now sits on. */
.menu-open .site-header {
  z-index: 130;
  /* Riding above the overlay means the header's own pale ground would paint a
     band across the top of it. Only the X should be left. */
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
.menu-open .site-header .shell > *:not(.menu-toggle) { opacity: 0; pointer-events: none; }
.menu-open .menu-toggle { color: var(--deep-ink); }

/* The three lines sit on the right on a phone in every language. Its place is
   a thumb-reach convention, not a reading-direction one — mirrored with the
   text it lands under the other hand each time the language changes, which is
   how it ended up somewhere nobody looked for it. Physical right, deliberately
   not inset-inline-end. */
/*
 * The three lines are the whole navigation now, so they are never conditional
 * on width. The horizontal bar they used to share the header with is gone: it
 * carried a treatments dropdown fed by a stored menu built with the language
 * filter off, so it hung eighty-odd items in four languages under one word,
 * and it was the reason the header overflowed between 1081 and 1280px. The
 * fullscreen panel says the same things with room to say them.
 */
@media (min-width: 1280px){
  .menu-toggle { margin-inline-start: auto; }
  [dir="rtl"] .menu-toggle { order: -1; margin-inline-start: 0; }
}

@media (max-width: 1279.98px){
  /*
   * Kept in the flow rather than pinned with position:absolute, because an
   * absolutely positioned toggle reserves no width — and in Arabic, where the
   * brand is also on the physical right, the logo and the three lines were
   * drawn on top of one another. Reserving the space with padding does not
   * work either: the label is "MENU" in three languages and "القائمة" in the
   * fourth, so the width to reserve is not a number anyone can write down.
   *
   * In English the row runs left to right, so an auto start-margin puts the
   * toggle at the far right. In Arabic the row runs the other way and being
   * last no longer means being on the right, so it is ordered first instead —
   * same physical corner, and it carries its own width with it.
   */
  .menu-toggle {
    margin-inline-start: auto;
  }
  [dir="rtl"] .menu-toggle {
    order: -1;
    margin-inline-start: 0;
  }
}

/*
 * On the narrowest phones the word does not fit beside the brand — at 320px
 * the button hung 50 pixels off the side of the screen. Clipped rather than
 * removed, because the word is the button's accessible name: display:none
 * would leave a screen reader announcing an unlabelled button, and the three
 * lines are only obvious if you can see them.
 */
@media (max-width: 399.98px){
  .menu-toggle .label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

.vh-menu {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: var(--deep);
  color: var(--deep-ink);
  display: grid;
  grid-template-columns: 1.1fr .95fr .75fr;
  visibility: hidden;
  clip-path: inset(0 0 100% 0);
  overflow: hidden;                 /* the sweep leaves through the bottom */
}

/* The sheet that leads the panel down and carries on past the bottom edge. */
.vh-menu__sweep {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: var(--teal);
  pointer-events: none;
  will-change: transform;
}
.vh-menu.is-open { visibility: visible; }
.vh-menu__inner {
  padding: clamp(90px, 12vh, 150px) clamp(24px, 6vw, 96px) 48px;
  overflow-y: auto;
  align-self: center;
}
.vh-menu__eyebrow {
  font-family: var(--f-mono);
  font-size: .66rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--teal-bright);
  margin-bottom: 26px;
}
.vh-menu__list { display: flex; flex-direction: column; gap: 2px; }
.vh-menu__item { overflow: hidden; }
.vh-menu__item > a {
  display: inline-flex;
  align-items: baseline;
  gap: 16px;
  padding: 6px 0;
  font-family: var(--f-display);
  font-size: clamp(2rem, 5.2vw, 3.6rem);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -.02em;
  color: var(--deep-ink);
  text-decoration: none;
  transform: translateY(105%);
  transition: color .3s var(--ease);
}
.vh-menu__item > a:hover,
.vh-menu__item > a:focus-visible { color: var(--teal-bright); }

/* The index is information — seven destinations, and which one you are on the
   way to — so it is set in the mono face at label size and never competes. */
.vh-menu__n {
  font-family: var(--f-mono);
  font-style: normal;
  font-size: .6rem;
  letter-spacing: .12em;
  color: var(--teal-bright);
  opacity: .55;
  transform: translateY(-.35em);
}
.vh-menu__item > a:hover .vh-menu__n,
.vh-menu__item > a:focus-visible .vh-menu__n { opacity: 1; }

/*
 * The treatments answer to the destination they belong to. Showing all
 * twenty-one the moment the panel opens made the eye start in the middle
 * column and the eight destinations — the thing the panel is for — read as a
 * caption beside them. They wait for "All treatments" to be pointed at or
 * focused instead, which is also what a reader expects of a list that sits
 * beside a menu item.
 *
 * Only where there is a pointer to hover with. Below 900px the panel is one
 * column and the list is simply the next thing down the page (see the narrow
 * rules further down), so it stays open there.
 */
@media (min-width: 901px) {
  .vh-menu__tx {
    opacity: 0;
    visibility: hidden;
    transform: translateX(-14px);
    transition: opacity .4s var(--ease), transform .5s var(--ease-out), visibility .4s;
  }
  [dir="rtl"] .vh-menu__tx { transform: translateX(14px); }
  .vh-menu.tx-open .vh-menu__tx {
    opacity: 1;
    visibility: visible;
    transform: none;
  }
  /* A hint that the item has something behind it. */
  .vh-menu__item > a[data-opens-treatments]::after {
    content: "";
    display: inline-block;
    width: .42em;
    height: .42em;
    margin-inline-start: .34em;
    border-radius: 50%;
    background: currentColor;
    opacity: .35;
    vertical-align: .28em;
    transition: opacity .3s var(--ease);
  }
  .vh-menu.tx-open .vh-menu__item > a[data-opens-treatments]::after { opacity: 1; }
}

/* The treatments, where the header dropdown used to be. */
.vh-menu__tx {
  padding: clamp(90px, 12vh, 150px) clamp(24px, 3vw, 40px) 48px;
  align-self: center;
  overflow-y: auto;
  max-height: 100vh;
  border-inline-start: 1px solid var(--deep-line);
}
.vh-menu__tx-list { display: flex; flex-direction: column; }
.vh-menu__tx .group-label {
  font-family: var(--f-mono);
  font-size: .6rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--deep-ink-3);
  margin: 18px 0 7px;
}
.vh-menu__tx .group-label:first-child { margin-top: 0; }
.vh-menu__tx a {
  color: var(--deep-ink-2);
  text-decoration: none;
  font-size: .92rem;
  line-height: 1.55;
  padding: 3px 0;
  transition: color .25s var(--ease);
}
.vh-menu__tx a:hover,
.vh-menu__tx a:focus-visible { color: var(--teal-bright); }

.vh-menu__meta {
  border-inline-start: 1px solid var(--deep-line);
  padding: clamp(90px, 12vh, 150px) clamp(24px, 4vw, 56px) 48px;
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.vh-menu__meta h3 {
  font-family: var(--f-mono);
  font-size: .64rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--deep-ink-3);
  font-weight: 400;
  margin-bottom: 10px;
}
.vh-menu__meta a { color: var(--deep-ink-2); text-decoration: none; }
.vh-menu__meta a:hover { color: var(--teal-bright); }
.vh-menu__preview {
  aspect-ratio: 4 / 3;
  border-radius: var(--r);
  overflow: hidden;
  background: var(--surface-2);
  position: relative;
}
.vh-menu__preview img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.06);
  transition: opacity .6s var(--ease), transform 1.2s var(--ease-out);
}
.vh-menu__preview img.is-active { opacity: 1; transform: none; }

@media (max-width: 1180px) {
  .vh-menu { grid-template-columns: 1.15fr .85fr; }
  .vh-menu__meta { display: none; }
}
@media (max-width: 900px) {
  /* One column, and the panel scrolls as a whole rather than in three
     separate wells — three nested scrollers on a phone is three ways to get
     stuck. The treatments keep their heading so the list is not mistaken for
     a continuation of the one above it. */
  .vh-menu {
    grid-template-columns: 1fr;
    align-content: start;
    overflow-y: auto;
  }
  .vh-menu__inner { overflow: visible; padding-bottom: 8px; }
  .vh-menu__tx {
    border-inline-start: 0;
    border-block-start: 1px solid var(--deep-line);
    padding: 26px clamp(24px, 6vw, 96px) 56px;
    overflow: visible;
    max-height: none;
    align-self: start;
  }
  .vh-menu__tx-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 20px;
  }
  .vh-menu__tx .group-label { grid-column: 1 / -1; }
}
@media (max-width: 420px) {
  .vh-menu__tx-list { grid-template-columns: 1fr; }
}

/* the toggle replaces the burger once the motion layer is live */
.has-motion .burger { display: none !important; }
.has-motion .menu-toggle { display: inline-flex; }
.has-motion .mobile-nav { display: none !important; }

/* --------------------------------------------------------------------------
   5. Pinned horizontal journey
   -------------------------------------------------------------------------- */
.journey-rail { position: relative; overflow: hidden; }
.journey-track {
  display: flex;
  gap: clamp(20px, 3vw, 40px);
  width: max-content;
  padding-inline: var(--gutter);
  align-items: stretch;
}
.journey-card {
  width: min(76vw, 460px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(24px, 3vw, 38px);
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.journey-card__media {
  border-radius: var(--r);
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--surface-2);
}
.journey-card__media img { width: 100%; height: 100%; object-fit: cover; }
.journey-card__n {
  font-family: var(--f-mono);
  font-size: .72rem;
  letter-spacing: .1em;
  color: var(--teal);
}
.journey-card h3 { font-size: var(--step-2); }
.journey-card p { color: var(--ink-2); font-size: .95rem; }

.journey-progress {
  height: 2px;
  background: var(--line);
  margin: 28px var(--gutter) 0;
  overflow: hidden;
}
.journey-progress span {
  display: block;
  height: 100%;
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* Below the pin breakpoint the track is never moved by anything: the pinned
   horizontal scrub is desktop-only, so on a phone four cards 1286px wide sat
   inside an overflow:hidden rail with no transform and no scroller — you saw
   cards one and two and the rest were unreachable. It reads as a section that
   stops halfway through, because that is what it was.

   Here it becomes what a row of cards should be on a touch screen: a snapping
   scroller you swipe. The card width already leaves the next one peeking, so
   the gesture advertises itself, and the progress bar under it follows the
   scroll instead of the pin. */
@media (max-width: 860.98px) {
  .journey-track {
    width: auto;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scroll-padding-inline: var(--gutter);
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .journey-track::-webkit-scrollbar { display: none; }
  .journey-card {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
}

/* --------------------------------------------------------------------------
   6. Card media parallax
   -------------------------------------------------------------------------- */
.js .svc-card .svc-media { overflow: hidden; }
.js .svc-card .svc-media img { will-change: transform; }

/* --------------------------------------------------------------------------
   6b. WebGL canvases

   Each one sits exactly on top of the <img> it is drawing, so the photograph
   underneath stays in the document — indexed, alt text intact, and visible the
   instant the canvas is removed or never appears at all.
   -------------------------------------------------------------------------- */
.vh-gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity .28s var(--ease);
}
.vh-gl.is-on { opacity: 1; }

.svc-media { position: relative; }
/* the shader does its own zoom under the pointer — the CSS one would only
   disagree with it for the length of the fade */
.has-gl .svc-card:hover .svc-media img { transform: none; }

/* the menu preview: once WebGL is drawing, the stacked <img>s are only there to
   be read, so take them out of the paint rather than blending them together */
.vh-menu__preview.has-gl img { opacity: 0 !important; transform: none !important; }
.vh-gl--preview { transition: none; }

/* --------------------------------------------------------------------------
   7. Page transition veil
   -------------------------------------------------------------------------- */
.vh-veil {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--deep);
  pointer-events: none;
  transform: scaleY(0);
  transform-origin: 50% 100%;
}

/* --------------------------------------------------------------------------
   8. Reduced motion — everything above collapses to a plain, static page
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .vh-cursor,
  .vh-cursor-dot,
  .vh-veil,
  .vh-gl { display: none !important; }
  .vh-menu__preview.has-gl img { opacity: 1 !important; }
  .has-cursor,
  .has-cursor a,
  .has-cursor button { cursor: auto; }
  .js .vh-line > span { transform: none !important; }
  .vh-menu__item > a { transform: none !important; }
  .vh-menu__sweep { display: none !important; }
  .vh-menu__tx,
  .vh-menu__meta > * { opacity: 1 !important; transform: none !important; }
  .hero-film canvas { opacity: 1; }
  .journey-track { width: auto; flex-wrap: wrap; }
  .journey-card { width: 100%; }
}
