/* ==========================================================================
   DIVLAB FOOTER — section 5, the lacquered pile
   --------------------------------------------------------------------------
   Scoped to the footer's own classes plus one variable for the Theme Builder
   bar. No resets, no element selectors, nothing global.
   ========================================================================== */

:root {
  /* The height of the Theme Builder bottom bar.

     THIS ONE VALUE DRIVES THREE THINGS: the section's bottom padding, how far
     the bar is lifted back over it, and — read at runtime by dvl-footer.js —
     where the pile's floor sits. Set it to your built bar's real height and
     the prisms come to rest exactly on top of it, never over the social
     links. There is no second number to keep in sync. */
  --dvl-bar-h: 70px;

  /* How far the canvas reaches ABOVE the section.

     This is what makes the services-to-footer fall visible. Each canvas
     clips at its own edge, so with the canvas inset to the section the
     entire crossing happened where neither canvas drew anything — the three
     prisms left #dvl-services, entered a region nothing was rendering, and
     appeared to fade into nothing. The canvas has to physically reach up
     into that region.

     The overhang is TRANSPARENT (the opaque stage plane inside the scene
     covers only the section), so #dvl-services is neither hidden nor made
     unclickable. It costs fill rate — dvl-footer.js drops maxDPR to
     compensate — so keep it just large enough for the fall to read. */
  --dvl-spawn-h: 70vh;

  /* The site's horizontal grid, in ONE place.
     It was written out four times — twice on the section, twice on the
     headline, which also carries a negative margin of the same size so the
     type still starts on the grid. Four hard-coded 80s cannot be moved at a
     breakpoint without moving all four together, and missing one puts the
     headline off the grid at exactly the width nobody tests. */
  --dvl-gutter: 80px;
}

/* ============================================== RESPONSIVE, TWO STOPS ====
   Elementor's own breakpoints — tablet <= 1024, mobile <= 767 — so the width
   where this stylesheet changes its mind is the width where the editor's
   tablet and mobile tabs take over. dvl-footer.js reads the same two numbers.

   Everything here is a variable, not a rule: the layout below is written
   once and re-proportions itself. */
@media (max-width: 1400px) and (min-width: 1025px) and (min-height: 541px),
       (hover: none) and (min-width: 1025px) and (min-height: 541px),
       (pointer: coarse) and (min-width: 1025px) and (min-height: 541px) {
  :root { --dvl-gutter: 80px; }
}

@media (max-width: 1024px) and (min-width: 768px) and (min-height: 541px) {
  :root { --dvl-gutter: 80px; }
}

@media (max-height: 540px) and (orientation: landscape) {
  :root {
    --dvl-gutter: calc(env(safe-area-inset-left, 0px) + 24px);
    --dvl-spawn-h: 0px;
  }
  .dvl-footer {
    padding-left: calc(env(safe-area-inset-left, 0px) + 24px) !important;
    padding-right: calc(env(safe-area-inset-right, 0px) + 24px) !important;
  }
  .dvl-footer-bar {
    padding-left: calc(env(safe-area-inset-left, 0px) + 24px) !important;
    padding-right: calc(env(safe-area-inset-right, 0px) + 24px) !important;
  }
}

@media (max-width: 767px) and (orientation: portrait) {
  :root {
    --dvl-gutter: 24px;
    --dvl-spawn-h: 0px;
  }
}

.dvl-footer {
  position: relative;

  /* ---- LOAD-BEARING. WITHOUT THIS THE SECTION IS INVISIBLE. ----
     dvl-stage.js puts a position:fixed, full-viewport canvas at z-index 0 on
     the BODY, as the last element in the document. A positioned element with
     `z-index: auto` paints in the SAME layer as one with `z-index: 0`, ties
     broken by DOM order — so that canvas paints over any section that has
     not explicitly raised itself, wherever it sits on the page.

     This is exactly what made #dvl-services render perfectly and show
     nothing at all. Do not remove it. */
  z-index: 2;
  margin-top: -2px !important;

  min-height: 100vh;
  min-height: 100svh;
  /* Room at the base for the bar that gets pulled up over it. The canvas
     still spans the FULL section, but nothing comes to rest in that strip
     because the physics floor is derived from --dvl-bar-h. */
  padding-bottom: var(--dvl-bar-h);
  box-sizing: border-box;
  /* VISIBLE. The canvas deliberately overhangs the top of this section by
     --dvl-spawn-h so the three arriving prisms can be drawn while they are
     still above it; hidden would crop exactly that strip and put the fall
     back where it cannot be seen. The overhang is transparent and takes no
     pointer events, so the section above is neither covered nor blocked. */
  overflow: visible;

  /* The last stop of the works → services → footer ramp, and #dvl-services'
     final recede tone. The join between the two sections is invisible
     because the two values are equal, not because anything animates across
     it. The stage plane inside the scene is painted this same colour, so
     those two surfaces meet without an edge either.

     var() with the literal as fallback: the canonical value lives in
     dvl-services.css so all three sections cannot drift apart, but this file
     still renders correctly on a page that has a footer and no services
     section. Changing the slate means changing it there — and also
     CONFIG.background.to in dvl-services.js and CONFIG.background.tone in
     dvl-footer.js, which paint the same colour into WebGL. */
  background-color: var(--dvl-tone-bottom, #c9d2d4);

  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-start;
  /* The site-wide horizontal grid. A variable, not a clamp: the headline
     below has to land on exactly the same number, and two clamps with
     different reference boxes drift apart at intermediate widths. */
  padding-left: var(--dvl-gutter);
  padding-right: var(--dvl-gutter);

  /* The section is the pointer target, so without this a touch drag starting
     here is swallowed and the page will not scroll — the footer becomes a
     trap at the bottom of the document. */
  touch-action: pan-y;
}

/* Elementor puts `transition: … , transform .4s` on every `.e-con`. A
   transition on a property a script rewrites per frame does not smooth it,
   it fights it. Nothing here is animated by CSS, so nothing is lost. */
.dvl-footer,
.dvl-footer-title {
  transition: none !important;
}

/* Once the headline has been baked into the scene it must stop painting in
   the DOM, or you get it twice. visibility, not display: the bake reads
   getBoundingClientRect() and the computed font off this element on every
   resize, so it has to keep its box and its typography. */
.dvl-footer--titled .dvl-footer-title {
  visibility: hidden;
}

/* ...EXCEPT INSIDE THE ELEMENTOR EDITOR, where hiding it makes the headline
   uneditable: you cannot click a heading you cannot see, and the WebGL copy
   is a texture with no handles on it. The editor is the one context where
   the DOM element has to win, so it is shown there and the baked copy sits
   behind it — slightly doubled while editing, correct on the front end.

   Elementor puts .elementor-editor-active on <body> inside the preview
   iframe. Edit the heading normally; the bake re-reads this element's text
   and computed font on the next resize, and on the published page. */
body.elementor-editor-active .dvl-footer--titled .dvl-footer-title {
  visibility: visible;

  /* ABOVE THE CANVAS, and this is not cosmetic. The enqueue gate is
     is_page('stage-test'), which is true for the editor's preview request as
     well — so the module builds in the editor, and its canvas (z-index 2)
     carries an OPAQUE stage plane across the whole section. Merely un-hiding
     a heading at z-index 1 leaves it painted behind that plane: still
     invisible, still unselectable, and indistinguishable from the rule not
     working. 30 clears the canvas (2) and the bar (20). */
  z-index: 30;

  /* The front end sets pointer-events: none so the headline cannot carve a
     dead rectangle out of the pile's interaction surface. In the editor that
     same line stops you clicking the widget to select it — you would have to
     reach it through the Navigator. Restored here only. */
  pointer-events: auto;
}

/* -------------------------------------------------------- the headline ---
   STYLED HERE, RENDERED IN THE SCENE, BEHIND THE GLASS.

   This element is the source of truth — its text, computed font, box and
   gradient are all read by buildStage(), which draws them into the opaque
   stage plane the prisms sit on top of and refract through. That is the only
   way glass can distort type: three.js builds the transmission pass from
   opaque scene geometry, and a DOM element can be painted before or after a
   canvas but never interleaved with objects inside it.

   The DOM copy is hidden the moment the bake succeeds (.dvl-footer--titled
   above). If WebGL fails or the font never resolves, the class is never
   added and this stays visible as ordinary styled text — which is why the
   gradient below is declared here as well as redrawn in the bake.

   pointer-events: none is essential. The section beneath is the interaction
   surface for the whole pile, and a text block lying across it would carve a
   dead rectangle out of the middle of the footer. */
.dvl-footer-title {
  position: relative;
  z-index: 1;
  pointer-events: none;

  margin: 0 0 clamp(28px, 7vh, 96px);
  max-width: 22ch;

  /* Matches the section's own grid. The section already pads by the gutter,
     so these are padding on the text block itself, cancelled by an equal
     negative margin — which is what lets the baked gradient run edge to edge
     while the glyphs still start on the grid. All three read the same
     variable, so a breakpoint moves them together or not at all. */
  padding-left: var(--dvl-gutter);
  padding-right: var(--dvl-gutter);
  margin-left: calc(-1 * var(--dvl-gutter));
  box-sizing: border-box;

  user-select: none;
  -webkit-user-select: none;
}

/* ---- DEFAULT TYPOGRAPHY, DELIBERATELY UNSPECIFIC ----
   :where() is not decoration. It gives this block a specificity of ZERO, so
   ANY Elementor typography — which is emitted against
   `.elementor-element-xxxx .elementor-heading-title` and lands on the <h2>
   inside the widget, not on the widget the class is on — wins without a
   fight, at every breakpoint, forever.

   Written as a normal rule these four lines were the headline's computed
   font on the wrapper, and the wrapper is what the bake used to read: a
   heading set to Inter 100px/230 rendered into the scene as the theme's
   Mynerve at 66.9px. They stay here only so a page with no Elementor styling
   at all — the local test harnesses, or a WebGL failure — still shows a
   sensibly sized headline. */
:where(.dvl-footer-title),
:where(.dvl-footer-title .elementor-heading-title) {
  font-size: clamp(38px, 6.6vw, 104px);
  line-height: 0.98;
  letter-spacing: -0.02em;
  font-weight: 200;
  font-weight: 230;
}

/* ---- DIAGONAL TEXT GRADIENT ----
   ON THE HEADING AS WELL AS THE WIDGET, and that pairing is load-bearing.
   -webkit-text-fill-color INHERITS; background-clip: text does not. So with
   the class on an Elementor widget wrapper, `transparent` reached the <h2>
   while the clipped background stayed on the wrapper — glyphs painted with
   nothing behind them, i.e. an invisible headline anywhere the bake did not
   run: the Elementor editor, a WebGL failure, an old browser.

   Declaring both means whichever element ends up holding the glyphs also
   holds the gradient they are clipped from.

   `color` stays declared and is not decorative: background-clip: text is
   unsupported in a handful of engines, and where it fails the fill colour is
   the only thing standing between the visitor and invisible type. It is the
   gradient's first stop, so the fallback is the darkest end rather than an
   unrelated colour.

   background-clip is paired with the -webkit- prefix because Safari still
   requires the prefixed form for text clipping. */
.dvl-footer-title,
.dvl-footer-title .elementor-heading-title {
  color: #3a301e;
  background: linear-gradient(340deg, #3a301e 0%, #9d988f 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* --------------------------------------------------------- the canvas ---
   Spans the whole section, including the strip the bar sits over — nothing
   rests there regardless, because the floor is derived from --dvl-bar-h.

   pointer-events: none, because there is nothing on the canvas to hit. The
   listeners live on the section, which converts coordinates into canvas
   space; that keeps the headline above it and the bar below it fully
   clickable without either of them stealing the pile's gestures. */
.dvl-footer__canvas {
  position: absolute;
  left: 0;
  right: 0;
  width: 100%;
  /* TALLER THAN THE SECTION, overhanging the top. This is the whole reason
     the crossing is visible: the three arriving prisms are drawn here while
     they are still above the footer. The overhang is transparent — the
     opaque stage plane inside the scene covers only the section itself. */
  top: calc(-1 * var(--dvl-spawn-h));
  height: calc(100% + var(--dvl-spawn-h));
  display: block;
  z-index: 2;
  pointer-events: none;
}

/* ==========================================================================
   THEME BUILDER BOTTOM BAR
   --------------------------------------------------------------------------
   Add the class `dvl-footer-bar` to the Theme Builder footer's OUTERMOST
   container.

   THE LIFT IS APPLIED BY THE SCRIPT, NOT BY A SIBLING SELECTOR — and that is
   not a preference. WordPress renders a Theme Builder footer as a sibling of
   the PAGE WRAPPER on <body>:

       BODY
       ├── HEADER.elementor-…
       ├── DIV.elementor-…        <- #dvl-footer lives in here
       └── FOOTER.site-footer     <- the bar renders here

   so `.dvl-footer ~ .dvl-footer-bar` can never match: the two are not
   siblings and never will be. Measured on the live page before this was
   caught. dvl-footer.js locates the bar anywhere in the document, reads its
   REAL height, writes --dvl-bar-h from it and applies the margin — which
   also means the height no longer has to be kept in sync by hand.

   The rule below is a no-JS fallback only. :has() is well supported now, but
   if it is unavailable the page simply shows the bar under the section
   rather than over it, which is untidy and not broken.
   ========================================================================== */
.dvl-footer-bar {
  position: relative;
  /* Above the canvas (2) and the headline (5). The social links and the
     email must be crisp DOM and fully clickable. */
  z-index: 20;

  /* TRANSPARENT, so it inherits whatever section it is lifted over — no
     per-page colour to keep in sync.

     Worth knowing what that costs elsewhere: on any page WITHOUT a footer
     section the lift does not apply and the bar sits in normal flow, where
     the hero's fixed full-viewport canvas is behind it at z-index 0. If you
     ever see glass drifting behind the email address on another template,
     that is this line, and the fix is a background on that page's last
     section rather than on the bar. */
  background: transparent !important;
}

html:has(.dvl-footer) .dvl-footer-bar {
  margin-top: calc(-1 * var(--dvl-bar-h));
}

/* ==========================================================================
   PHONE — THE HEADLINE SITS IN THE UPPER THIRD, NOT ON THE FLOOR
   --------------------------------------------------------------------------
   Elementor has the inner container as a flex column with
   `justify-content: flex-end`, which pins the headline to the bottom of the
   section. Measured at 375x812 its top landed at 502px — 61.8% down — while
   home-mobile-footer puts it at y 283 of 874, or 32.4%. On the live build it
   reads as crushed against the prism cluster with all the empty space above
   it.

   Anchored from the TOP rather than re-balanced with centring, because the
   headline's height is about to change: it is 50px/60 today and the design
   calls for 48px/57.6, and it wraps to three lines either way. A top offset
   holds the first baseline where the design puts it regardless of what the
   block below it grows to.

   svh, matching the section's own min-height on line 87 — the two have to
   agree or the offset drifts against the box it is measured in when the URL
   bar collapses. 32.4svh of 812 is 263px, which is the design's 283 scaled
   by 375/402.

   Desktop is untouched: there the headline sits at y 407 of 1081 and the
   flex-end placement already produces it.
   ========================================================================== */
@media (max-width: 767px) {
  .dvl-footer .e-con-inner {
    justify-content: flex-start !important;
    padding-top: 32.4svh;
  }

  /* ==== THE HEADLINE IS REAL TEXT HERE, NOT A TEXTURE ==================
     dvl-footer.js's phone profile sets background.domTitle, so the bake
     skips the headline and .dvl-footer--titled is never added — which
     leaves this element visible instead of visibility:hidden.

     ABOVE THE CANVAS (z-index 3 against the canvas's 2), and that is
     forced by the transmission pass, not chosen. The stage plane behind
     the prisms must stay opaque or the glass has nothing to refract, and
     that plane is painted on the canvas — so a headline left at z-index 1
     would be covered by it. The consequence is that prisms pass behind the
     type rather than over it; on a phone the pile sits below and right of
     the headline, so there is little to see.

     pointer-events stays none, inherited from the base rule: the section
     underneath is the tap surface that scatters the pile, and a text block
     lying across it would carve a dead rectangle out of the middle. */
  .dvl-footer-title {
    z-index: 3;
  }

  /* The gradient the bake used to draw, as CSS.

     Same angle and same two stops as CONFIG.background — titleAngle 340,
     titleFrom #3a301e, titleTo #9d988f — and the numbers agree directly
     because the canvas version was written to reproduce a CSS 340deg
     gradient in the first place.

     ---- WHY THE ID, WHICH THIS FILE OTHERWISE AVOIDS -------------------
     A background-image on an Elementor page is fought over by two rules
     that both carry !important:

       theme reset   *, ::after, ::before { background-image: initial }
       Elementor     .e-con.e-parent:nth-of-type(n+2):not(.e-lazyloaded)…
                     { background-image: none }

     The second is container background lazy-loading. Measured here: a class
     selector WITH !important still computed to `none`; adding the section's
     ID was what made it resolve. There is already a gradient declared for
     .dvl-footer-title further up this file and it has been dead for exactly
     this reason — it only ever mattered on desktop, where the headline is
     hidden behind the bake and nobody saw it.

     background-clip: text needs a transparent fill to show through, and
     -webkit-text-fill-color is the property that actually wins on WebKit:
     `color: transparent` alone loses to Elementor's own colour rule on the
     h2. Both are set so either engine resolves it.

     Both the wrapper and .elementor-heading-title are listed because
     Elementor's typography lands on the inner element while the class sits
     on the widget wrapper, and which one carries the text depends on the
     widget used. */
  #dvl-footer.dvl-footer--domtitle .dvl-footer-title,
  #dvl-footer.dvl-footer--domtitle .dvl-footer-title .elementor-heading-title {
    background-image: linear-gradient(340deg,
                        var(--dvl-title-from, #3a301e),
                        var(--dvl-title-to,   #9d988f)) !important;
    -webkit-background-clip: text !important;
    background-clip: text !important;
    color: transparent !important;
    -webkit-text-fill-color: transparent !important;
  }
}

/* ---- THE BAR IS ON THE SAME GRID AS EVERYTHING ELSE --------------------
   The bar is a Theme Builder container and arrives with whatever padding
   Elementor's editor last had — measured on the live page that was 80px on
   desktop (right by luck) and 10px on mobile (wrong), which put the social
   icons at x 16 and the email 10px off the right edge while the footer
   section above them sat correctly at 24.

   Driven from --dvl-gutter, so the bar now tracks the section it is lifted
   over at all three stops — 80 / 48 / 24 — and there is no fourth copy of
   the number to fall out of sync.

   Nested containers are flattened for the same reason as in the works and
   services sections: Elementor stacks a default 10px on each level, and the
   gutter has to be the distance to the GLYPH, not to a box that then insets
   it again. Horizontal only — the bar's vertical padding sets its height,
   which dvl-footer.js measures into --dvl-bar-h. */
.dvl-footer-bar {
  padding-left: var(--dvl-gutter) !important;
  padding-right: var(--dvl-gutter) !important;
}

.dvl-footer-bar .e-con-inner,
.dvl-footer-bar .e-con-full,
.dvl-footer-bar .e-con-boxed {
  padding-left: 0 !important;
  padding-right: 0 !important;
}

.dvl-footer-bar .e-con-inner {
  max-width: none !important;
}

/* ==========================================================================
   ELEMENTOR'S CONTENT-WIDTH CAP DOES NOT APPLY HERE
   --------------------------------------------------------------------------
   Elementor caps every .e-con-inner at the global content width and centres
   it. On this section that is min(100%, 1440px), and measured at 1920 it put
   the headline at x 233 against a design value of 89 — the section's own
   80px gutter was correct and was simply being overridden by a narrower,
   centred box inside it.

   Same defect the services section had at 1320px. The gutter belongs to the
   section; nothing nested inside it should be re-deciding the width.

   Unscoped by breakpoint on purpose: the cap is min(100%, 767px) at mobile,
   which does not bind at phone widths but does at tablet, and the tablet
   brief is the same design at adjusted sizes — not a second, narrower one.
   ========================================================================== */
.dvl-footer .e-con-inner {
  max-width: none !important;
}

/* ------------------------------------------------------------ fallbacks - */

/* No WebGL, or the .glb never arrived: headline and bar only, which is a
   perfectly reasonable footer. */
.dvl-footer:not(.dvl-footer--ready) .dvl-footer__canvas {
  display: none;
}

/* Touch: the pile stays — it is most of the visual — and simply rests. There
   is no hover on touch, so the sweep cannot exist, and taps fall through to
   the section, which is harmless. */
@media (hover: none) {
  .dvl-footer__canvas {
    pointer-events: none;
  }
}

/* NOTE: deliberately no `prefers-reduced-motion` rule killing the pointer.
   An earlier version did, and on a machine with Windows animations turned
   off that made the whole pile inert while everything else looked normal —
   an invisible, total failure indistinguishable from a broken build. The
   pile is motionless unless the visitor touches it, which is already what
   reduced-motion asks for. */

/* ==========================================================================
   MOBILE LANDSCAPE (viewport height <= 540px)
   --------------------------------------------------------------------------
   Match footer typography to hero title: size, leading, tracking, and styling.
   ========================================================================== */
@media (max-height: 540px) and (orientation: landscape) {
  :root {
    --dvl-bar-h: 50px;
    --dvl-spawn-h: 0px;
  }
  .dvl-footer {
    padding-bottom: var(--dvl-bar-h);
    padding-top: max(32px, calc(env(safe-area-inset-top) + 20px)) !important;
    justify-content: flex-start !important;
  }
  .dvl-footer .e-con-inner {
    justify-content: flex-start !important;
    padding-top: 0 !important;
    margin-top: 0 !important;
  }
  .dvl-footer .dvl-footer-title,
  .dvl-footer .dvl-footer-title *,
  .dvl-footer .elementor-heading-title,
  .dvl-footer .elementor-heading-title *,
  .dvl-footer h2 {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
    font-size: var(--dvl-hero-title-size, clamp(28px, 6.2vh, 42px)) !important;
    line-height: var(--dvl-hero-title-leading, 1.15) !important;
    letter-spacing: var(--dvl-display-tracking, -0.061em) !important;
    font-weight: 200 !important;
    font-weight: 230 !important;
    font-style: normal !important;
    -webkit-font-smoothing: antialiased !important;
    -moz-osx-font-smoothing: grayscale !important;
    max-width: 65vw !important;
    padding-top: 8px !important;
    margin-top: 0 !important;
  }
}

