/* ════════════════════════════════════════════════════════════
   EPIC HORIZONS — THE VINE
   css/vine.css  (pairs with js/vine.js)

   ONE thick winding stem that runs down the MIDDLE of the homepage,
   top to bottom, drawn with stroke-dashoffset scrubbed to scroll and
   carrying a leaf every ~520px. The leaves breathe in a slow breeze;
   the stem never moves on its own.

   Everything here is decorative: pointer-events are off throughout and
   the markup carries aria-hidden, so selection, reading order and hit
   testing are exactly what they were.

   No filter: blur() anywhere — banned on this project (measured jank).
   No transition on anything the scrub writes: a stylesheet transition on
   a scroll-driven property makes the browser re-ease every frame.
   ════════════════════════════════════════════════════════════ */

/* ── THE LAYER ───────────────────────────────────────────────────
   Absolutely positioned at the document origin and sized by script to
   exactly the document height measured BEFORE insertion, so its bottom
   coincides with the page's own bottom and document.body.scrollHeight
   cannot move.

   z-index 26 is measured, not chosen. Every full-bleed band on this
   page paints an opaque background from inside its own stacking
   context, so there is no slot between a section's background and its
   text — a layer is either above the whole section or invisible behind
   it. 26 is the lowest value that clears every one of them: the two
   pinned scenes are the high-water marks (.eh-corridor at z 25, whose
   own children run to z 300 inside it, and .wow at z 3). It still sits
   under every piece of fixed chrome — the grain overlay (45), the
   sticky call bar (900), the review badge (990), the sticky CTA (998),
   the mobile nav (1001) and the cursor (9998) — so the vine is
   overlaid by the same grain as the rest of the page and can never
   cover a control.

   contain: layout paint keeps a 34,000px-tall child from participating
   in the page's layout or paint bounds. */
.ehv-host {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  /* UNDER the page, not over it. The painting site's trail sits behind every
     section (its stage z-indexes are 20-30, the trail is 4); this was at 26,
     which put it on top of the content. Sections here carry z-index 2-3, the
     corridor 25, and the fixed background field is 0 — so 1 is the one slot
     that is above the background and below everything the visitor reads. */
  /* 24, not 1. Truly behind the sections means invisible: unlike the painting
     site — whose stages are dark panels over a canvas — almost every section
     here paints an opaque background, so a vine underneath them only shows in
     the gaps. It sits above them instead, but faded and masked to zero wherever
     it crosses a text run, which is what makes it read as running UNDER the
     content rather than over it. */
  /* THE MIDDLE LAYER — one of three, and the whole point of the tier system:
       section backgrounds      no z-index, root order
       the vine                 1
       containers, cards, media, buttons   2  (css/vine.css, below)
     It only works because nothing between the vine and those cards creates a
     stacking context of its own. */
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
  contain: layout paint;
}

/* The SVG is only as wide as the meander plus one leaf's reach — about
   560px, not the full 1440 — so the rasterised area per tile stays
   small even though the element is the height of the document. */
.ehv-svg {
  position: absolute;
  top: 0;
  display: block;
  pointer-events: none;
  overflow: visible;
}

/* The stem is drawn by scroll only. Nothing here may animate it. The
   halo is a <use> of the same path, so it inherits the dash geometry
   and grows with it without a second write. */
.ehv-line,
.ehv-halo {
  transition: none;
  animation: none;
}

/* Leaves are revealed by the scrub as the growing tip reaches them, so
   nothing is visible ahead of the vine. The transform on .ehv-leaf is
   written as an ATTRIBUTE by the scrub; the breeze below uses the CSS
   transform property on the nested .ehv-leaf__b, so the two compose
   instead of overwriting each other. */
.ehv-leaf {
  opacity: 0;
  transition: none;
}

/* ── THE BREEZE ─────────────────────────────────────────────────
   The one idle motion the vine has, and it is CSS only: no rAF loop, no
   per-frame JavaScript, nothing measured. Amplitude is ±2.6° about the
   leaf's own attachment point with a 7–12s period and a per-leaf
   negative delay set inline, so neighbours are always out of phase and
   the row never pulses in unison. Past about four degrees it stops
   reading as air and starts reading as an animation.

   transform-box: fill-box puts the origin inside the leaf's own
   bounding box; 0% 50% is the attachment end, which is where a leaf
   actually pivots. Without both, the origin would be the centre of a
   34,000px-tall viewBox and every leaf would swing across the page.

   No will-change: these are small, they sit inside a `contain: layout
   paint` host, and only the two or three inside the viewport are ever
   rasterised. Promoting sixty of them to their own layers costs more
   memory than the repaint it saves. */
@keyframes ehv-breeze {
  0%   { transform: rotate(-2.6deg); }
  50%  { transform: rotate(2.6deg); }
  100% { transform: rotate(-2.6deg); }
}

.ehv-leaf__b {
  transform-box: fill-box;
  transform-origin: 0% 50%;
  animation: ehv-breeze 9s ease-in-out infinite;
}


/* ── PHONES ─────────────────────────────────────────────────────
   No vine at or below 767px. A phone has no spare width: the content
   column is the whole screen, so a stem down the middle would sit on
   every line of type with nowhere to fade to. The page is finished
   without it. js/vine.js never builds it at these widths either — this
   rule is the belt to that braces, so a later change to the script
   cannot leak a half-built layer onto a phone. */
/* The vine now runs on phones too — it is the site's signature effect and the
   whole reason mobile felt flatter than desktop. It is built at phone-scaled
   geometry (see js/vine.js) so it stays inside a 390px screen. */


/* ── REDUCED MOTION ─────────────────────────────────────────────
   The vine is a scroll-driven drawing with no meaningful static state —
   at rest it is however far the last scroll drew it — so it is removed
   outright, and with it the breeze. The breeze is killed by name as
   well as by the host being display:none, so a later change to the host
   cannot quietly reintroduce idle motion here. */
@media (prefers-reduced-motion: reduce) {
  .ehv-host { display: none !important; }
  .ehv-leaf__b { animation: none !important; }
}


/* SCENES OPT OUT ENTIRELY. #services (the 3D corridor), the gallery and the
   sticky scenes position their own content absolutely inside stages; forcing
   `position: relative` on a card in there detaches it from the frame it is
   supposed to fill. The corridor panels showed a photo with an empty box under
   it for exactly this reason. Those sections already sit above the vine as
   whole units, so they need nothing from this block. */
html.lenis #services .container,
html.lenis #wall-of-work .wow__card,
html.lenis #wall-of-work img,
html.lenis .scene .container {
  /* `position: unset` — NOT `static`. These elements are positioned by their
     own stylesheet (corridor.css puts .service-card at `position: absolute;
     inset: 0` so it fills its frame); forcing `static` detached it just as
     badly as forcing `relative` did, and left the photo floating above an
     empty box. `unset` drops my declaration entirely and lets theirs win. */
  position: unset;
  z-index: unset;
}

/* ── THE TIER ABOVE THE VINE ──────────────────────────────────
   Everything a visitor reads or clicks sits above it. Declared once, here,
   rather than per section — the layering was inconsistent precisely because
   it had been set section by section as each effect was added.
   ──────────────────────────────────────────────────────────── */
/* ANY .container, at any depth — not just a direct child of a section.
   js/s3d.js moves each section's content into a .s3d__in wrapper, so the
   container is a grandchild, and the direct-child rule missed it. Those
   containers kept the stylesheet's `z-index: 1`, which TIES with the vine —
   and a tie is broken by DOM order, which the vine wins because its host is
   appended to the body last. That is what put it over the timelapse progress
   bar. */
html.lenis main .container,
html.lenis .stat-ticker .container,
html.lenis .areas-strip .container,
html.lenis .s3d__in,
html.lenis .project-card,
html.lenis .guarantee-card,
html.lenis .area-card,
html.lenis .faq-item,
html.lenis .cm-card,
html.lenis .calc-card,
html.lenis .testimonial-card,
html.lenis .pt-step,
html.lenis .ba-slider,
html.lenis .btn,
html.lenis img,
html.lenis figure {
  position: relative;
  /* !important because the slant rules in style.css set `z-index: 1` on
     containers with a higher-specificity selector (.section--dark.slant-both >
     .container). Losing that cascade leaves the container TIED with the vine at
     1, and a tie goes to whoever is later in the DOM — the vine. This is the
     one place the override has to be unconditional. */
  z-index: 2 !important;
}

/* ── STRIP THE SECTION-LEVEL STACKING CONTEXTS ────────────────
   The slant system sets `z-index: 2` on every slanted section so its
   ::before background can sit under its own .container. That z-index makes
   the whole section a stacking context, which is why the vine ended up
   BELOW every one of them and ABOVE the loose images that sit outside one —
   the exact inverse of what it should do.

   Sections do not need it: they already overlap in DOM order, and their
   ::before still sits under their container because the container is tier 2
   above. The three scroll scenes keep theirs — their stages genuinely depend
   on it — and the vine simply passes behind those.
   ──────────────────────────────────────────────────────────── */
html.lenis main > section:not(#services):not(#wall-of-work):not(.scene):not(.eh-corridor-tail),
html.lenis .stat-ticker,
html.lenis .areas-strip {
  z-index: auto;
}

/* ── Full-bleed photo wrappers belong in tier 2 ───────────────
   .photo-split__img is `position: absolute; z-index: 0`. That is a stacking
   context, so lifting the <img> inside it to tier 2 changes nothing — the
   whole wrapper still sits at 0, under the vine. The photo band above
   "We've Heard It All Before" was exactly this case. Any wrapper that
   positions a photo has to be lifted, not the photo.
   ──────────────────────────────────────────────────────────── */
html.lenis .photo-split__img,
html.lenis .photo-split__panel,
html.lenis .photo-float-wrap,
html.lenis .hero__bg {
  z-index: 2;
}

/* ── Overlays sit above the image they caption ────────────────
   Lifting every <img> into tier 2 put the gallery photos on top of their own
   figcaptions, which are absolutely positioned with no z-index of their own —
   the captions vanished. Anything that overlays a photo goes to tier 3.
   ──────────────────────────────────────────────────────────── */
/* Overlays go to tier 3 by z-index ALONE. Declaring `position: relative` here
   was the same error as on the cards: .service-card__body is positioned at the
   bottom of its card by its own stylesheet, and relative dropped it into flow —
   which is why the label ended up floating in the middle of the photo. These
   elements are already positioned; they only ever needed the stacking value. */
html.lenis figcaption,
html.lenis .wow__card figcaption,
html.lenis .project-card__body,
html.lenis .service-card__body,
html.lenis [class*="__caption"],
html.lenis [class*="__label"],
html.lenis .photo-split__panel {
  z-index: 3;
}
