/* ==========================================================================
   DIVLAB STAGE — styles
   Scoped to the stage's own classes/ids. Safe to paste into Elementor's
   Custom CSS or a child-theme stylesheet. No resets, no element selectors.
   ========================================================================== */

/* ==========================================================================
   DESIGN TOKENS — declarations only, never overrides
   --------------------------------------------------------------------------
   These are VALUES for Elementor to reference, not rules that fight it.
   Nothing here targets an Elementor selector, so nothing here can take a
   control away from the panel.

   That distinction is the whole workflow on this site. Every typographic
   value in the header and the hero is already authored per-breakpoint in
   Elementor and emitted to post-221.css / post-228.css. A stylesheet rule
   specific enough to beat those would silently kill four breakpoint settings
   and make the panel lie — which is exactly what happened to the footer
   headline earlier in this project, where a default in this codebase was
   overriding the editor and the WebGL bake was reading the wrong element as
   a result. Tokens in, overrides out.
   ========================================================================== */
:root {
  /* The design file's own token names, adopted verbatim so the Figma
     variables and the site variables are the same words. Both were read out
     of the mobile hero's own gradient declaration, which resolves to
     linear-gradient(-39.5deg, var(--divlab-font-dark) .3%,
                               var(--logo-light) 107%) */
  --divlab-font-dark: #3a301e;
  --logo-light: #9d988f;

  /* ---- HERO HEADLINE SIZE ----
     Tuned to land on EXACTLY 120px at a 1920px viewport, which is the width
     the design is drawn at, while keeping the 100px the build already uses
     at 1440 — the commonest desktop width, and a value that was deliberately
     chosen rather than inherited.

     Derivation: a straight line through (1440, 100) and (1920, 120).
         slope = (120 - 100) / (14.4 - 19.2) vw-units = 4.1667
         intercept = 120 - 4.1667 x 19.2 = 40
     so size = 40px + 4.1667vw, clamped to [100px, 120px].

         1200 ->  90 (clamped to 100)
         1440 -> 100
         1680 -> 110
         1920 -> 120
         2560 -> 147 (clamped to 120)

     The clamp's lower bound only matters above Elementor's 1200px
     breakpoint; below that the panel's own steps (80 / 65 / 50px) take over
     and are untouched. Enter this in Elementor as a Custom unit —
     var(--dvl-hero-title-size) — so the control still belongs to the editor. */
  --dvl-hero-title-size: clamp(100px, 40px + 4.1667vw, 120px);

  /* Mobile, from home-mobile-1 (402x874). Elementor's own <=767 step holds
     this one — a clamp floored at 100px cannot serve a phone — so the token
     exists to keep the number documented in the same place as its desktop
     sibling rather than only in the editor. */
  --dvl-hero-title-size-mobile: 48px;

  --dvl-hero-title-leading: 1.1;          /* 132px at 120  (desktop) */
  --dvl-hero-title-leading-mobile: 1.2;   /*  57.6px at 48 (mobile)  */

  /* ---- THE DISPLAY TRACKING IS ONE NUMBER AT EVERY SIZE ----
     Measured independently at both breakpoints and they agree exactly:
         desktop  -7.32px  / 120px = -0.061em
         mobile   -2.928px /  48px = -0.061em
     Which is why it must be set in em. As px it would have to be re-derived
     for every breakpoint and would drift the first time a size changed. */
  --dvl-display-tracking: -0.061em;

  /* ---- SECTION 2 NARRATIVE COPY ----
     From home-mobile-4/5/6, which are identical. Deliberately a different
     tracking from the display face above (-0.04 against -0.061) — this is
     reading copy at 28px, not a headline, and it is set looser on purpose.

     opacity 0.80 is in the design, not an accident: the copy is meant to sit
     slightly back from the glass. Worth knowing while fixing the washed-out
     bug — the target is softened, just nowhere near as far as the gradient
     was taking it. Flat --divlab-font-dark, no gradient. */
  --dvl-s2-title-size-mobile: 28px;
  --dvl-s2-title-leading-mobile: 1.607;   /* 45px at 28px */
  --dvl-s2-title-tracking: -0.04em;
  --dvl-s2-title-opacity: 0.8;
}

/* ==========================================================================
   SCROLL BEHAVIOUR — deliberate exception to the "no global styles" rule.
   --------------------------------------------------------------------------
   Hello Elementor sets `scroll-behavior: smooth` on <html>. That makes the
   BROWSER animate scroll position on its own, while ScrollTrigger is scrubbing
   against the position it is fighting for. The two run at different rates and
   the result is visible stutter through every pinned section.

   GSAP documents this as incompatible with ScrollTrigger. It is the one rule
   here that must be global, because the property only has an effect on the
   scrolling element itself.
   ========================================================================== */
html {
  scroll-behavior: auto !important;
  overscroll-behavior-y: none;
}

body {
  overscroll-behavior-y: none;
}

/* The single shared canvas. FIXED and full-viewport: it sits behind every
   Elementor section and never scrolls, which is what lets one WebGL scene
   span the whole page.
   pointer-events:none is essential — Elementor content above it stays fully
   clickable, and the stage listens on window for the cursor instead.       */
.dvl-stage__canvas {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100lvh;
  bottom: auto !important;
  right: auto !important;

  /* ---- 100lvh WITH bottom:auto PREVENTS STRETCH / SQUISH ON MOBILE ----
     inset:0 or bottom:0 anchors the canvas bottom to the mobile browser's
     dynamic viewport. As the user scrolls, the address bar collapses or
     expands, moving the bottom edge and dynamically altering the canvas DOM
     box height. When the DOM box height changes while WebGL drawing buffer
     dimensions stay fixed, the browser GPU hardware-stretches and squishes
     the canvas pixels in real-time on every touch scroll.

     By fixing top:0, left:0, height:100lvh and bottom:auto !important, the
     canvas DOM box stays strictly immutable at the large viewport height,
     completely eliminating all scroll-induced stretching and squishing. */
  display: block;
  z-index: 0;             /* behind content, above the page background */
  pointer-events: none;
}

/* Elementor sections that host stage content must sit ABOVE the canvas and
   be transparent so it shows through. Add this class to the container. */
.dvl-section {
  position: relative;
  z-index: 1;
  background: transparent !important;
}

/* ==========================================================================
   NEVER TRANSITION A PINNED SECTION
   --------------------------------------------------------------------------
   Elementor attaches a CSS transition to `transform` on its containers.
   ScrollTrigger repositions a released pin by writing an INLINE transform,
   and a running CSSTransition overrides that inline value while it animates:
   the inline style is present in the style attribute, but the element still
   computes to identity and simply does not move.

   The result is that at the moment the hero unpins, the section stays put
   instead of travelling, then snaps to catch up. The baked text planes track
   their element's viewport rect, so the headline lurches a full pin-length in
   one frame — which is the "text disappears for several frames and pops back"
   flicker at the handoff.

   Measured on production, across the unpin at a 4px scroll step:
     with Elementor's transition   max text-plane jump = 1170px
     with this rule                max text-plane jump =    4px

   These sections are driven frame-by-frame by scroll. Nothing about them
   should ever be interpolated by CSS.

   ---- THE HEADER HAS THE SAME DEFECT, AND IT WAS MISSING FROM THIS LIST --
   .dvl-header is an .e-con too, so it carries the same transform transition,
   and initHeader() drives it with a SCRUBBED trigger — an inline transform
   rewritten every frame. Measured on the live page at scrollY 3000, with the
   trigger reporting progress 1:

     inline style   transform: translate3d(0px, -58px, 0px)
     computed       matrix(1, 0, 0, 1, 0, 0)
     rect top       0

   The header never left. Writing `top: -58px` instead moved it immediately,
   which proves nothing else was holding it — and no CSS rule matched
   `transform` on the element at all, so the cascade was not the culprit.
   getAnimations() named it outright: a running CSSTransition on `transform`.

   That is the "sticky header leaks over the works, services and footer
   content on mobile" report. It is not a mobile bug and not a z-index bug;
   the header simply never animates out, at any width. */
.dvl-section,
.pin-spacer > .dvl-section,
.dvl-header,
header.elementor-location-header {
  transition: none !important;
}

header.elementor-location-header {
  position: absolute !important;
  top: 0 !important;
  left: 0 !important;
  right: 0 !important;
  height: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  pointer-events: none !important;
  z-index: 100 !important;
}

.dvl-header,
header.elementor-location-header .dvl-header,
.dvl-header a,
.dvl-header button,
.dvl-header .dvl-burger,
header.elementor-location-header a,
header.elementor-location-header button {
  pointer-events: auto !important;
}

#dvl-hero {
  margin-top: 0 !important;
  top: 0 !important;
}

/* Each pinned section needs a full viewport height for ScrollTrigger.
   svh is correct HERE — a pinned section must fit on screen in the worst
   case, which is when the URL bar is expanded. It is a stable number that
   does not move when the bar collapses, so the pin length never shifts
   mid-scroll. Only the CANVAS above needed the large viewport. */
#dvl-hero,
#dvl-section-2 {
  min-height: 100vh;
  min-height: 100svh;
}

/* ==========================================================================
   THE HERO HEADLINE NEEDS ROOM FOR ITS OWN LINE BREAKS
   --------------------------------------------------------------------------
   The headline's four lines are forced with <br> in Elementor, and
   bindText() in dvl-stage.js re-wraps any forced segment that does not FIT
   its box — a deliberate fallback, so desktop breaks carried down to a phone
   degrade to a natural wrap instead of overflowing the baked texture.

   The problem was the margin. Measured on the live page in the tablet band,
   where Elementor's own step sets the headline to 80px:

       box                                       640px
       "Presence For Studios,"   in Inter        631px      <-  9px to spare
       "Worth Understanding"     in Inter        630px      <- 10px to spare
       "Presence For Studios,"   in Arial        707px      <- 67px OVER

   Nine pixels is not a tolerance, it is a coincidence. Any difference in
   metrics - a font version, a fallback standing in while Inter is still in
   flight - re-splits two of the four lines and the headline renders as six:

       We Shape Digital / Presence For / Studios, /
       Brands, Founders / Worth / Understanding

   The widget is 700px wide and spends 60px of that on right padding, which
   is empty space beside a left-aligned headline. Reclaiming it takes the
   box to 700 and the margin from 9px to 69px.

   THE WIDGET'S OWN WIDTH IS UNTOUCHED, so no sibling moves: the text box
   grows into padding that is already inside the widget. The real fix for the
   font-in-flight case is the re-bake in dvl-stage.js (rebakeOnLateFonts);
   this is the headroom that stops the measurement being a coin toss in the
   first place. */
#dvl-hero .elementor-widget-heading[data-dvl-text],
#dvl-hero [data-dvl-text],
#dvl-hero .elementor-heading-title {
  padding-right: 0 !important;
}

@media (min-width: 768px) {
  #dvl-hero .elementor-widget-heading[data-dvl-text],
  #dvl-hero [data-dvl-text],
  #dvl-hero .elementor-heading-title,
  #dvl-section-2 .elementor-widget-heading[data-dvl-text],
  #dvl-section-2 [data-dvl-text],
  #dvl-section-2 .elementor-heading-title {
    width: max-content !important;
    max-width: 90vw !important;
  }
}

/* ==========================================================================
   SECTION 3 MUST OUTRANK THE CANVAS
   --------------------------------------------------------------------------
   The stage canvas is position:fixed at z-index 0 and is the LAST element in
   <body>. A positioned element with `z-index: auto` paints in the SAME layer
   as one with `z-index: 0`, with ties broken by DOM order — so the canvas
   paints over any section that has not explicitly raised itself, wherever
   that section sits on the page.

   #dvl-section-2, #dvl-services and #dvl-footer were each given a z-index
   for exactly this reason. #dvl-works never was, and it is `position:
   relative; z-index: auto`.

   Measured on the live page with hit-testing, which follows paint order:
   at a point over the second work card the canvas came back at index 8 and
   #dvl-works at index 12 — the canvas ON TOP. The card <img> sat at index 9,
   also under it. Some Elementor widgets in the section carry their own
   z-index and floated above (indices 0-7), which is why the CAPTIONS stayed
   visible while the ARTWORK vanished — the section looked half-rendered
   rather than obviously covered.

   DELIBERATELY NO BACKGROUND. Raising the section brings its own content out
   from behind the canvas, which is the bug. Painting it opaque would also
   hide the canvas's own backdrop, and that backdrop is not incidental here —
   measured across this section it runs rgb(235,234,232) at the top of the
   viewport to rgb(214,193,195) at the bottom, the residual warm tint from
   Section 2 fading out. That gradient is the section's background by design
   on desktop today. Add `background: #eae9e7` here if you would rather the
   section be flat and opaque, but that is a design change, not part of this
   fix.
   ========================================================================== */
#dvl-works {
  position: relative;
  z-index: 10;
}

/* Text authored in Elementor and baked into the WebGL layer.
   It stays in the DOM for SEO, screen readers and text selection; the stage
   sets color:transparent once it has baked it, so it renders only inside
   the scene where the glass can refract it. Until then it is visible, which
   keeps the page readable if WebGL never starts. */
[data-dvl-text] {
  position: relative;
  z-index: 1;
}

/* WebGL failed → un-hide the DOM type so the page still reads correctly. */
.dvl-stage-fallback [data-dvl-text] {
  color: inherit !important;
  -webkit-text-fill-color: currentColor !important;
}
.dvl-stage-fallback .dvl-stage__canvas {
  display: none;
}

/* ==========================================================================
   ELEMENTOR THEME BUILDER — header / footer
   --------------------------------------------------------------------------
   The canvas is position:fixed at z-index 0. Anything that must appear over
   it needs a stacking context of its own and a higher z-index. Elementor's
   Theme Builder parts do not get one by default, so the header would sit
   BEHIND the glass without these rules.

   Class names below are Elementor's own theme-part wrappers. Add
   `dvl-header` / `dvl-footer` to your header/footer container in the Theme
   Builder as well, so the rules keep working if Elementor renames anything.
   ========================================================================== */

/* --- HEADER: overlays the hero, transparent, always on top --------------- */
.dvl-header,
header.elementor-location-header {
  position: relative;
  z-index: 100;                    /* well above the canvas (0) */
}

/* Overlay variant: the header floats ON the hero instead of pushing it down,
   so the hero still fills a true 100vh and the logo sits over the glass.

   FIXED, not absolute. Absolute scrolls the header away the instant the page
   moves, while the hero underneath is pinned — the two come apart. Fixed
   holds it at the top for the whole hero pin, and initHeader() in
   dvl-stage.js then translates it out exactly as the hero unpins, so they
   leave together. Do not change this to absolute without removing that
   trigger, or the header will never come back on screen.

   Remove this block if you want a normal header that occupies its own space. */
.dvl-header--overlay,
body.dvl-stage-overlay-header header.elementor-location-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background: transparent !important;
  will-change: transform;
}

/* THE SAFE-AREA INSET, COPIED SOMEWHERE SCRIPT CAN READ IT.

   initHeader() in dvl-stage.js has to translate the header far enough to
   clear the notch, and env() cannot be read with getComputedStyle on the
   property itself — but a custom property holding it can. Zero on anything
   without a notch, so the arithmetic is unchanged on those devices.

   On :root rather than the header, because the header is inside a stacking
   and containment context that Elementor owns, and a custom property set
   there would be one more thing to keep alive through a re-render. */
:root {
  --dvl-safe-top: env(safe-area-inset-top, 0px);
}

/* --- FOOTER: must be opaque, or the fixed canvas shows through it -------- */
.dvl-footer,
footer.elementor-location-footer {
  position: relative;
  z-index: 2;
  /* Set a real background in Elementor. Without one you will see the glass
     scene continuing behind the footer, because the canvas never scrolls. */
}

/* --- Sticky headers -----------------------------------------------------
   Elementor's "Sticky" motion effect switches the header to position:fixed
   mid-scroll, which can fight a ScrollTrigger pin. If you use it, this keeps
   it above the canvas in both states.                                     */
.elementor-sticky--active {
  z-index: 100 !important;
}

/* ==========================================================================
   RESPONSIVE OVERRIDES FOR STAGE / HERO
   ========================================================================== */

/* 1. TABLET LANDSCAPE (768px - 1400px landscape, e.g. iPad landscape) */
@media (max-width: 1400px) and (min-width: 768px) and (orientation: landscape) and (min-height: 541px),
       (hover: none) and (min-width: 768px) and (orientation: landscape) and (min-height: 541px),
       (pointer: coarse) and (min-width: 768px) and (orientation: landscape) and (min-height: 541px) {
  :root {
    --dvl-hero-title-size: clamp(76px, 6.5vw, 92px);
    --dvl-hero-title-leading: 1.12;
  }
  #dvl-hero .elementor-heading-title,
  #dvl-hero h1,
  #dvl-hero [data-dvl-text] {
    font-size: var(--dvl-hero-title-size) !important;
    line-height: var(--dvl-hero-title-leading) !important;
    text-align: right !important;
    width: max-content !important;
    max-width: 90vw !important;
  }
  #dvl-section-2 .elementor-widget-heading,
  #dvl-section-2 [data-dvl-text],
  #dvl-section-2 .elementor-widget-container {
    width: max-content !important;
    max-width: 90vw !important;
    margin-left: auto !important;
    margin-right: auto !important;
  }
  #dvl-section-2 .elementor-heading-title,
  #dvl-section-2 h2,
  #dvl-section-2 p {
    font-size: clamp(24px, 3.2vw, 34px) !important;
    line-height: 1.35 !important;
    text-align: center !important;
    padding-left: 20px !important;
    padding-right: 20px !important;
    box-sizing: border-box !important;
  }
}

/* 2. TABLET PORTRAIT (768px - 1080px portrait, e.g. iPad portrait) */
@media (max-width: 1080px) and (min-width: 768px) and (orientation: portrait),
       (hover: none) and (min-width: 768px) and (orientation: portrait),
       (pointer: coarse) and (min-width: 768px) and (orientation: portrait) {
  :root {
    --dvl-hero-title-size: clamp(66px, 7.5vw, 84px);
    --dvl-hero-title-leading: 1.14;
    --dvl-s2-title-size-mobile: clamp(24px, 3.2vw, 32px);
  }
  #dvl-hero .elementor-heading-title,
  #dvl-hero h1,
  #dvl-hero [data-dvl-text] {
    font-size: var(--dvl-hero-title-size) !important;
    line-height: var(--dvl-hero-title-leading) !important;
    text-align: right !important;
    width: max-content !important;
    max-width: 90vw !important;
  }
  #dvl-section-2 .elementor-widget-heading,
  #dvl-section-2 [data-dvl-text],
  #dvl-section-2 .elementor-widget-container {
    width: max-content !important;
    max-width: 90vw !important;
    margin-left: auto !important;
    margin-right: auto !important;
  }
  #dvl-section-2 .elementor-heading-title,
  #dvl-section-2 h2,
  #dvl-section-2 p {
    font-size: clamp(22px, 3vw, 28px) !important;
    line-height: 1.38 !important;
    text-align: center !important;
    padding-left: 20px !important;
    padding-right: 20px !important;
    box-sizing: border-box !important;
  }
}

/* 3. MOBILE LANDSCAPE (Landscape with small viewport height <= 540px) */
@media (max-height: 540px) and (orientation: landscape) {
  :root {
    --dvl-hero-title-size: clamp(28px, 6.2vh, 42px);
    --dvl-hero-title-leading: 1.15;
    --dvl-s2-title-size-mobile: clamp(18px, 4.4vh, 22px);
  }
  #dvl-hero,
  #dvl-section-2 {
    min-height: 100vh;
    min-height: 100svh;
    padding-top: max(20px, env(safe-area-inset-top));
    padding-bottom: 20px;
    touch-action: pan-y;
  }
  /* Section 2: cleanly wrap narrative copy into exactly 3 lines with no orphan word */
  #dvl-section-2 .elementor-widget-heading,
  #dvl-section-2 [data-dvl-text],
  #dvl-section-2 .elementor-widget-container {
    width: min(92vw, 820px) !important;
    max-width: 820px !important;
    margin-left: auto !important;
    margin-right: auto !important;
  }
  #dvl-section-2 .elementor-heading-title,
  #dvl-section-2 h2,
  #dvl-section-2 p {
    font-size: clamp(18px, 4.4vh, 22px) !important;
    line-height: 1.45 !important;
    text-align: center !important;
  }
}

/* 4. MOBILE PORTRAIT (<= 767px, orientation portrait) */
@media (max-width: 767px) and (orientation: portrait) {
  :root {
    --dvl-hero-title-size: clamp(40px, 10.4vw, 48px);
    --dvl-hero-title-leading: 1.16;
  }
  #dvl-hero .elementor-heading-title,
  #dvl-hero h1,
  #dvl-hero [data-dvl-text] {
    font-size: var(--dvl-hero-title-size) !important;
    line-height: var(--dvl-hero-title-leading) !important;
    text-align: right !important;
  }
  #dvl-section-2 {
    min-height: 100vh;
    min-height: 100svh;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
  }
  #dvl-section-2 .elementor-widget-heading,
  #dvl-section-2 [data-dvl-text],
  #dvl-section-2 .elementor-widget-container {
    width: min(90vw, 360px) !important;
    max-width: 90vw !important;
    margin-left: auto !important;
    margin-right: auto !important;
    padding-left: 10px !important;
    padding-right: 10px !important;
    box-sizing: border-box !important;
  }
  #dvl-section-2 .elementor-heading-title,
  #dvl-section-2 h2,
  #dvl-section-2 p {
    font-size: clamp(18px, 4.8vw, 21px) !important;
    line-height: 1.45 !important;
    text-align: center !important;
  }
}

