/* ==========================================================================
   DIVLAB AGENCY — the /agency page, five stages on one scroll
   --------------------------------------------------------------------------
   Scoped to #dvl-agency and to the two chrome classes dvl-agency.js sets on
   <body>. No resets, no bare element selectors, nothing global.

   THE FIVE STAGES, and which of them are scrubbed

     1  HERO        "Agency" alone over a steep, narrow glass prism —
                    "Shape, Test, Build" is HIDDEN at 0% scroll. The first
                    scroll swings the prism down to the diagonal and the
                    tagline rises in beside it. The prism's turn is
                    scrubbed; its idle drift is not, because an idle is a
                    thing that happens while you are not scrolling.
     2  LIFT        the prism rotates up and out; the lockup rises to sit
                    under the header; the grey card enters from below.
     3  CARD        the card fills the viewport, the headline and then the
                    line under it reveal from their own vertical centres;
                    the card then HOLDS long enough to be read, and recedes
                    into the page as Services arrives.
     4  SERVICES    two columns, four tagged items. Ordinary flow, and it
                    begins exactly where the stage ends - there is no blank
                    screen in that seam.
     5  OUTRO       dark ground, vector line grid, the closing headline, and
                    the header/footer inversion.

   Stages 1-3 all happen inside ONE STICKY viewport (.dvl-agency__act,
   inside the tall .dvl-agency__stage). Stages 4 and 5 are ordinary document
   flow with reveal triggers.

   WHAT THIS FILE LOOKS LIKE WITH NO JAVASCRIPT
   Everything is visible and readable. Every start-state that hides something
   is written under `.dvl-agency--ready`, which only dvl-agency.js adds, and
   only once it has GSAP and a measured layout. If the module 404s, the page
   is a long static page with all its copy on it rather than a blank screen.

   WHAT IS TUNABLE WITHOUT TOUCHING THIS FILE
   Every colour, font, size and rhythm is a custom property on #dvl-agency.
   Elementor's Typography and Colour pickers cannot reach any of it — they
   only emit CSS for widgets Elementor itself renders, and this page is one
   HTML widget — so a single Custom CSS block is the supported way in:

       #dvl-agency { --dvl-agency-ink: #2b2620; --dvl-agency-card: #d6dcdb; }

   See deploy/functions-agency-append.php for the full list.
   ========================================================================== */


/* ==========================================================================
   THE TRANSPARENT CHROME
   --------------------------------------------------------------------------
   The Theme Builder header and footer float OVER this page instead of
   taking room in it. The hero is a full-viewport sticky stage; a header in
   normal flow would push it down by its own height and the sequence would
   start with the prism already half off the top of the screen.

   Scoped to body.dvl-agency-page — set by dvl-agency.js, and only on this
   page — so enqueuing this stylesheet site-wide cannot reposition the header
   anywhere else. Both selectors are here because Elementor's wrapper class
   is the reliable one and `.dvl-header` is what survives Elementor renaming
   it; that is the same pairing dvl-stage.css and dvl-contact.css use.

   NO BACKGROUND IS FORCED. Whatever the header carries in the Theme Builder
   still applies — if it has an opaque background set there, it will still be
   opaque here and the mesh will be cut off by a band. Clear it in the Theme
   Builder, not here, or this page and every other page disagree about what
   the header is.
   ========================================================================== */
body.dvl-agency-page header.elementor-location-header,
body.dvl-agency-page .dvl-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  background: transparent;
}

/* ==========================================================================
   THE FOOTER — the social row and the email address over stage 5
   --------------------------------------------------------------------------
   IT STAYS IN NORMAL FLOW and is pulled back up over the outro by exactly
   its own height. It is NOT positioned, and the first build's `position:
   absolute; bottom: 0` is the bug this replaces.

   WHY THAT FAILED, because it looks right and is not: an absolutely
   positioned element resolves against its nearest POSITIONED ancestor, and
   <body> is not positioned. So it resolved against the initial containing
   block — which is the viewport-sized box at the TOP of the document, not
   the document — and `bottom: 0` put the footer one screen down the page,
   where it slid into view over the hero and sat there for the rest of the
   scroll. Making <body> relative would fix it, but this stylesheet has no
   business positioning the document for every other page that loads it.

   Normal flow plus a negative margin needs no positioned ancestor, cannot
   escape the document, and puts the footer exactly where it belongs: over
   the bottom of the dark section, visible only once the visitor is there.
   dvl-agency.js measures the footer and writes --dvl-agency-footer-h; the
   fallback below is only used for the frame before that runs.

   z-index, so it sits over the outro's grid rather than under it, and a
   transparent background so the dark ground shows through. */
body.dvl-agency-page footer.elementor-location-footer,
body.dvl-agency-page .elementor-location-footer,
body.dvl-agency-page [data-elementor-type="footer"],
body.dvl-agency-page #site-footer,
body.dvl-agency-page body > footer,
body.dvl-agency-page .dvl-footer-bar {
  position: relative;
  z-index: 10;
  margin-top: calc(-1 * var(--dvl-agency-footer-h, 96px)) !important;
  margin-bottom: 0 !important;
  background: transparent !important;
}

body.dvl-agency-page footer.elementor-location-footer *,
body.dvl-agency-page .elementor-location-footer *,
body.dvl-agency-page [data-elementor-type="footer"] *,
body.dvl-agency-page #site-footer *,
body.dvl-agency-page body > footer *,
body.dvl-agency-page .dvl-footer-bar * {
  background-color: transparent !important;
}


/* ==========================================================================
   THE SOLID CHROME BAR - MOBILE ONLY
   --------------------------------------------------------------------------
   A transparent header is right over the hero and right over the dark
   closing panel: both are broad, quiet fields with nothing to read behind
   the wordmark. It is wrong over Services, where body copy at a 24px gutter
   scrolls straight underneath it and collides with the logo and the burger.

   Top padding on the section does not fix that - it clears the first line
   and every line after it still passes behind. So the header takes a
   background for exactly the stretch where there is text behind it, and
   gives it back for the dark panel. dvl-agency.js owns `is-solid-chrome`
   (see buildSolidChromeTrigger); nothing else sets it.

   DESKTOP IS DELIBERATELY EXCLUDED. There the gutters are wide and the
   header sits clear above the column, so a bar would be a band across a
   composition that does not need one.

   backdrop-filter is progressive: where it is unsupported the rgba
   background alone still separates the type, which is the part that
   matters. -webkit- prefix for older iOS.
   ========================================================================== */
@media (max-width: 767px) {
  /* :not(.is-dark-chrome) is load-bearing, not defensive. The two classes
     can both be on for a frame at the boundary - a trigger's end and the
     next trigger's start are the same scroll position - and a white 82%
     bar behind type that has just inverted to #f7f6f2 is a white bar with
     white text on it, i.e. no header at all. Excluding it in the selector
     is deterministic; relying on the two triggers never overlapping is
     not. */
  body.dvl-agency-page.is-solid-chrome:not(.is-dark-chrome) header.elementor-location-header,
  body.dvl-agency-page.is-solid-chrome:not(.is-dark-chrome) .dvl-header {
    background: var(--dvl-chrome-bar, rgba(255, 255, 255, 0.82));
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
  }
}

/* ==========================================================================
   THE INVERSION
   --------------------------------------------------------------------------
   Stage 5 puts near-black under a header that was drawn for white. The class
   `is-dark-chrome` goes on <body> when the outro reaches the top of the
   viewport and comes off on the way back up; dvl-agency.js owns it, via a
   ScrollTrigger toggleClass, and nothing else ever sets it.

   IT IS A VARIABLE SWAP, NOT A SET OF OVERRIDES. One property changes and
   everything that reads it follows, so a new element in the header inherits
   the behaviour by using var(--dvl-chrome-ink) rather than by being added to
   a selector list here.

   THE TRANSITION IS ON THE PROPERTIES, NOT ON THE CLASS. Custom properties
   do not interpolate (they are not registered), so transitioning
   --dvl-chrome-ink would step. The transition lives on `color` and `fill`
   further down, which do interpolate, and the variable swap drives them.

   THE LOGO. Two shapes are covered because the Theme Builder can render a
   site logo either way: an inline <svg> (its paths take currentColor) or an
   <img src="*.svg"> (which cannot be recoloured from outside at all, so it
   is inverted with a filter instead). If the logo is a PNG photograph the
   filter will look wrong — swap it for an SVG, or set
   --dvl-chrome-logo-filter: none and supply a second logo in the Theme
   Builder's own conditional.
   ========================================================================== */
body.dvl-agency-page {
  /* The footer is NOT inside #dvl-agency, so its height has to be a property
     of <body> rather than of the section. dvl-agency.js writes both. */
  --dvl-agency-footer-h: 96px;
  --dvl-agency-dark: #3A301E;
  --dvl-chrome-ink: #2f2a21;          /* over white: stages 1-4 */
  --dvl-chrome-ink-dim: #6f6a61;
  --dvl-chrome-logo-filter: none;
  --dvl-chrome-fade: 520ms;

  /* The Theme Builder footer's padding, overridden for this page only - see
     the block above. Raise these if the social row sits too close to the
     bottom edge of the dark panel. */
  --dvl-chrome-footer-pad-t: 26px;
  --dvl-chrome-footer-pad-b: 26px;

  /* The mobile header's background while Services is behind it. */
  --dvl-chrome-bar: rgba(255, 255, 255, 0.82);
}

body.dvl-agency-page.is-dark-chrome {
  --dvl-chrome-ink: #f7f6f2;          /* over #3A301E: stage 5 */
  --dvl-chrome-ink-dim: #b9b3a8;
  --dvl-chrome-logo-filter: invert(1) brightness(1.9);
  background-color: var(--dvl-agency-dark, #3A301E) !important;
}

body.dvl-agency-page.dvl-menu-open {
  --dvl-chrome-ink: #f2f2f2 !important;
  --dvl-chrome-logo-filter: brightness(0) invert(0.949) !important;
}

/* Paired with the rule above so the page and the chrome change together
   rather than one chasing the other. */
body.dvl-agency-page {
  transition: background-color var(--dvl-chrome-fade) cubic-bezier(0.4, 0, 0.2, 1);
}

/* WHY THIS LIST IS LONG AND WHY IT CARRIES !important.
   It was four short selectors without it, and on the live site the result
   was that the LOGO inverted and the navigation and the email did not - so
   the dark section showed a white logo and nothing else, with the menu still
   sitting there in dark ink on dark ground.

   The reason is that the logo is reached through `fill` and `filter`, which
   Elementor never writes, while the type is reached through `color`, which
   it writes for every widget it renders. Elementor emits per-element rules
   of the form

       .elementor-1234 .elementor-element-abc123 .elementor-item { color: ... }

   which outranks anything scoped with a body class and an element name. A
   more specific selector alone will not reliably win either, because that
   generated specificity depends on how the template happens to be nested.
   !important is the correct tool here and this is the case it exists for:
   one page deliberately overriding a site-wide theme decision, for the
   duration of one section.

   THE CLASS NAMES ARE ELEMENTOR'S OWN, and they are listed explicitly
   rather than swept up with `*`. A universal selector would also flatten
   any accent colour a future header widget carries, which would be a much
   harder bug to find than this one was.

   NOTE THIS ALSO APPLIES IN THE LIGHT SECTIONS, where --dvl-chrome-ink is
   the dark value. That is intentional: it means the header reads the same
   from this page's stylesheet in both states, instead of being Elementor's
   colour until the class flips and this file's colour afterwards. If the two
   ever disagree you would see a jump at the top of the page rather than a
   fade at the bottom. */
body.dvl-agency-page header.elementor-location-header,
body.dvl-agency-page [data-elementor-type="header"],
body.dvl-agency-page #site-header,
body.dvl-agency-page header.elementor-location-header a,
body.dvl-agency-page header.elementor-location-header a:hover,
body.dvl-agency-page header.elementor-location-header a:focus,
body.dvl-agency-page header.elementor-location-header .elementor-item,
body.dvl-agency-page header.elementor-location-header .elementor-item:hover,
body.dvl-agency-page header.elementor-location-header .elementor-item-active,
body.dvl-agency-page header.elementor-location-header .elementor-heading-title,
body.dvl-agency-page header.elementor-location-header .elementor-icon,
body.dvl-agency-page header.elementor-location-header p,
body.dvl-agency-page header.elementor-location-header span,
body.dvl-agency-page header.elementor-location-header li,
body.dvl-agency-page header.elementor-location-header i,
body.dvl-agency-page [data-elementor-type="header"] a,
body.dvl-agency-page [data-elementor-type="header"] .elementor-item,
body.dvl-agency-page [data-elementor-type="header"] span,
body.dvl-agency-page [data-elementor-type="header"] p,
body.dvl-agency-page footer.elementor-location-footer,
body.dvl-agency-page [data-elementor-type="footer"],
body.dvl-agency-page #site-footer,
body.dvl-agency-page .elementor-location-footer,
body.dvl-agency-page footer.elementor-location-footer a,
body.dvl-agency-page footer.elementor-location-footer a:hover,
body.dvl-agency-page footer.elementor-location-footer a:focus,
body.dvl-agency-page footer.elementor-location-footer .elementor-item,
body.dvl-agency-page footer.elementor-location-footer .elementor-heading-title,
body.dvl-agency-page footer.elementor-location-footer .elementor-icon,
body.dvl-agency-page footer.elementor-location-footer .elementor-social-icon,
body.dvl-agency-page footer.elementor-location-footer p,
body.dvl-agency-page footer.elementor-location-footer span,
body.dvl-agency-page footer.elementor-location-footer li,
body.dvl-agency-page footer.elementor-location-footer i,
body.dvl-agency-page [data-elementor-type="footer"] a,
body.dvl-agency-page [data-elementor-type="footer"] span,
body.dvl-agency-page [data-elementor-type="footer"] p,
body.dvl-agency-page [data-elementor-type="footer"] i,
body.dvl-agency-page #site-footer a,
body.dvl-agency-page #site-footer span,
body.dvl-agency-page #site-footer p,
body.dvl-agency-page #site-footer i,
body.dvl-agency-page .dvl-header a,
body.dvl-agency-page .dvl-header span,
body.dvl-agency-page .dvl-footer-bar a,
body.dvl-agency-page .dvl-footer-bar span {
  color: var(--dvl-chrome-ink) !important;
  transition: color var(--dvl-chrome-fade) cubic-bezier(0.4, 0, 0.2, 1);
}

/* Burger lines color inversion */
body.dvl-agency-page.is-dark-chrome:not(.dvl-menu-open) .dvl-burger__line,
body.is-dark-chrome:not(.dvl-menu-open) .dvl-burger__line,
body.dvl-agency-page.is-dark-chrome:not(.dvl-menu-open) .dvl-header .dvl-burger__line {
  background: var(--dvl-chrome-ink, #f7f6f2) !important;
  background-color: var(--dvl-chrome-ink, #f7f6f2) !important;
}

/* Elementor's social icons are often a FONT glyph in an <i>, not an SVG, and
   a font glyph takes `color`, not `fill` - so the svg block below misses
   them entirely. */
body.dvl-agency-page footer.elementor-location-footer .elementor-social-icon i,
body.dvl-agency-page footer.elementor-location-footer .elementor-icon i,
body.dvl-agency-page [data-elementor-type="footer"] .elementor-social-icon i,
body.dvl-agency-page [data-elementor-type="footer"] .elementor-icon i,
body.dvl-agency-page #site-footer .elementor-social-icon i,
body.dvl-agency-page #site-footer .elementor-icon i,
body.dvl-agency-page header.elementor-location-header .elementor-icon i,
body.dvl-agency-page [data-elementor-type="header"] .elementor-icon i,
body.dvl-agency-page #site-header .elementor-icon i {
  color: var(--dvl-chrome-ink) !important;
}

/* Inline SVG — icons and a logo drawn as real paths. */
body.dvl-agency-page header.elementor-location-header svg,
body.dvl-agency-page header.elementor-location-header svg path,
body.dvl-agency-page [data-elementor-type="header"] svg,
body.dvl-agency-page [data-elementor-type="header"] svg path,
body.dvl-agency-page #site-header svg,
body.dvl-agency-page #site-header svg path,
body.dvl-agency-page footer.elementor-location-footer svg,
body.dvl-agency-page footer.elementor-location-footer svg path,
body.dvl-agency-page [data-elementor-type="footer"] svg,
body.dvl-agency-page [data-elementor-type="footer"] svg path,
body.dvl-agency-page #site-footer svg,
body.dvl-agency-page #site-footer svg path {
  fill: var(--dvl-chrome-ink) !important;
  transition: fill var(--dvl-chrome-fade) cubic-bezier(0.4, 0, 0.2, 1);
}
body.dvl-agency-page header.elementor-location-header svg [stroke]:not([stroke="none"]),
body.dvl-agency-page [data-elementor-type="header"] svg [stroke]:not([stroke="none"]),
body.dvl-agency-page #site-header svg [stroke]:not([stroke="none"]),
body.dvl-agency-page footer.elementor-location-footer svg [stroke]:not([stroke="none"]),
body.dvl-agency-page [data-elementor-type="footer"] svg [stroke]:not([stroke="none"]),
body.dvl-agency-page #site-footer svg [stroke]:not([stroke="none"]) {
  stroke: var(--dvl-chrome-ink);
  transition: stroke var(--dvl-chrome-fade) cubic-bezier(0.4, 0, 0.2, 1);
}

/* <img src="logo.svg"> — whole logo image filter flip */
body.dvl-agency-page header.elementor-location-header img,
body.dvl-agency-page [data-elementor-type="header"] img,
body.dvl-agency-page #site-header img,
body.dvl-agency-page footer.elementor-location-footer img,
body.dvl-agency-page [data-elementor-type="footer"] img,
body.dvl-agency-page #site-footer img {
  filter: var(--dvl-chrome-logo-filter);
  -webkit-filter: var(--dvl-chrome-logo-filter);
  transition: filter var(--dvl-chrome-fade) cubic-bezier(0.4, 0, 0.2, 1);
}

body.dvl-agency-page.dvl-menu-open header.elementor-location-header img,
body.dvl-agency-page.dvl-menu-open [data-elementor-type="header"] img,
body.dvl-agency-page.dvl-menu-open #site-header img,
body.dvl-agency-page.dvl-menu-open .dvl-header img {
  filter: brightness(0) invert(0.949) !important;
  -webkit-filter: brightness(0) invert(0.949) !important;
}

@media (prefers-reduced-motion: reduce) {
  body.dvl-agency-page { --dvl-chrome-fade: 1ms; }
}


/* ==========================================================================
   THE FOOTER'S OWN SPACING, ON THIS PAGE ONLY
   -------------------------------------------------------------------------- */
body.dvl-agency-page footer.elementor-location-footer,
body.dvl-agency-page [data-elementor-type="footer"],
body.dvl-agency-page #site-footer,
body.dvl-agency-page .elementor-location-footer,
body.dvl-agency-page footer.elementor-location-footer > .elementor > .e-con,
body.dvl-agency-page footer.elementor-location-footer > .elementor > .e-con > .e-con-inner,
body.dvl-agency-page footer.elementor-location-footer > .elementor > .elementor-section > .elementor-container,
body.dvl-agency-page [data-elementor-type="footer"] > .elementor > .e-con,
body.dvl-agency-page [data-elementor-type="footer"] > .elementor > .e-con > .e-con-inner,
body.dvl-agency-page [data-elementor-type="footer"] > .elementor > .elementor-section > .elementor-container,
body.dvl-agency-page #site-footer > .elementor > .e-con,
body.dvl-agency-page #site-footer > .elementor > .e-con > .e-con-inner,
body.dvl-agency-page #site-footer > .elementor > .elementor-section > .elementor-container {
  padding-top: var(--dvl-chrome-footer-pad-t, 26px) !important;
  padding-bottom: var(--dvl-chrome-footer-pad-b, 26px) !important;
}


/* ==========================================================================
   SECTION — the variables & ANTI-JITTER RESETS
   --------------------------------------------------------------------------
   THE RESETS IN THIS FILE:
   1. Box-sizing reset so 100vh / padding calculations do not overflow.
   2. Anti-jitter transition reset: GSAP controls transforms on scroll.
      CSS transitions on transforms or all properties (from Elementor or themes)
      will fight GSAP frame-by-frame, causing intense shudder on touch/tablet.
   ========================================================================== */
#dvl-agency,
#dvl-agency *,
#dvl-agency *::before,
#dvl-agency *::after {
  box-sizing: border-box;
}

#dvl-agency,
#dvl-agency .dvl-agency__stage,
#dvl-agency .dvl-agency__act,
#dvl-agency .dvl-agency__act *,
#dvl-agency .dvl-agency__lockup,
#dvl-agency .dvl-agency__title,
#dvl-agency .dvl-agency__word,
#dvl-agency .dvl-agency__tagline,
#dvl-agency .dvl-agency__w,
#dvl-agency .dvl-agency__card,
#dvl-agency .dvl-agency__card-frame,
#dvl-agency .dvl-agency__card-inner,
#dvl-agency .dvl-agency__card-head,
#dvl-agency .dvl-agency__lede,
#dvl-agency .dvl-agency__sub,
#dvl-agency .dvl-agency__ln,
#dvl-agency .dvl-agency__lni,
#dvl-agency .dvl-agency__mesh,
#dvl-agency .dvl-agency__wash,
#dvl-agency .dvl-agency__tile-canvas,
#dvl-agency .dvl-agency__tile-gl {
  transition: none !important;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

#dvl-agency {
  /* ---- palette -------------------------------------------------------- */
  --dvl-agency-paper: #ffffff;
  /* THE KIT'S OWN TOKENS, measured off the live site rather than sampled
     from a screenshot. Both are the SAME hue; "light" is simply the dark one
     at 50% alpha, which is why the gradient on "Shape, Test, Build" reads as
     one colour fading rather than as two colours meeting.

       Divlab font dark   #3A301E    Elementor --e-global-color-54ba015
       Divlab font light  #3A301E80  Elementor --e-global-color-3f48712

     If the kit changes, these are the two values to change. */
  --dvl-agency-ink: #3A301E;          /* Divlab font dark                   */
  --dvl-agency-mute: #3A301E80;       /* Divlab font light - the mono tags  */

  /* The design system's two named tokens, spelled as Figma spells them, so a
     handoff can be checked against the inspector without translating names.
     They are the two stops of the headline gradient further down. */
  --dvl-color-font-dark: #3A301E;     /* "Divlab font dark"                 */
  --dvl-color-font-light: #3A301E80;  /* "Divlab font light"                */
  --dvl-agency-card: #dce1e0;         /* the grey card, stage 3             */

  /* Stage 5's ground: the warm dark olive-brown from the design, and the
     value the comp actually samples. (An earlier pass had #1e1b18 from the
     written brief, which is a neutral charcoal — against it the grid went
     grey and the page lost the warmth every other section carries.) The grid
     and the headline are both expressed against this, so changing it means
     re-checking --dvl-agency-grid's alpha. */
  --dvl-agency-dark: #3A301E;
  --dvl-agency-dark-ink: #f7f6f2;
  /* The closing headline is pure white in the comp, a shade brighter than
     the chrome's #f7f6f2. Separate property so the two cannot be conflated:
     the spec sheet only calls out the headline. */
  --dvl-agency-outro-ink: #FFFFFF;
  --dvl-agency-grid: rgba(247, 246, 242, 0.075);
  --dvl-agency-grid-step: 190px;      /* the vector grid's cell             */

  /* The pink. Stage 1's wash sits behind the mesh and carries the whole
     composition if WebGL never starts, so it is a real gradient rather than
     a flat tint. */
  --dvl-agency-blush: #f0c7d5;
  --dvl-agency-blush-deep: #dfa2ba;
  --dvl-agency-blush-pale: #fbeef3;

  /* ---- type ----------------------------------------------------------- */
  /* INTER, and the fallbacks only matter before it loads. */
  --dvl-agency-font-display: "Inter", "Helvetica Neue", Helvetica, Arial,
                             sans-serif;
  /* "IBM_Plex_Mono" WITH UNDERSCORES. That is how the family is registered
     in this site's @font-face, and "IBM Plex Mono" with spaces does not
     match it - the browser silently falls through to the next family in the
     list and the mono type renders in something else, which looks like a
     weight problem rather than a missing font. Both spellings are listed so
     the file is also correct on a machine with the Google-hosted family
     installed. */
  --dvl-agency-font-mono: "IBM_Plex_Mono", "IBM Plex Mono", ui-monospace,
                          "SFMono-Regular", Menlo, Consolas,
                          "Liberation Mono", monospace;

  /* Fluid everywhere, and all of it clamped at both ends: the hero lockup
     has to survive a 1280 laptop and a 2560 display without the tagline
     colliding with the word "Agency". The middle terms are vw, so they
     track the gutter rather than the font size. */
  /* ---- THE FIGMA DESKTOP FRAME IS 1920px WIDE ------------------------
     Every size below is `clamp(floor, Xvw, figma-px)` where X is the Figma
     px divided by 19.2. At exactly 1920 the vw term equals the Figma value
     and the page matches the comp to the pixel; below that it scales down;
     above it the max holds it. That is why the vw numbers look arbitrary -
     they are 80/19.2, 100/19.2 and so on, not round numbers chosen by eye.

     The frame width was derived from the inspector screenshots rather than
     assumed: page3's text box reads 1759px and measures 769 of the frame's
     838 on screen, which puts the frame at 1916; page1's 910px box at 399
     of 836 puts it at 1907. Both round to 1920.

     The floors are NOT from Figma - there is no mobile comp yet. They are
     holding values so nothing collapses before that pass. */
  --dvl-agency-display: clamp(40px, 4.1667vw, 80px);    /* "Agency" 80      */
  --dvl-agency-tagline: clamp(30px, 5.2083vw, 100px);   /* "Shape…" 100     */
  --dvl-agency-lede: clamp(30px, 4.1667vw, 80px);       /* card headline 80 */
  /* The card's second line is DISPLAY type, not body copy — in the comp it
     is about 94% of the headline, not a third of it. It was small when the
     card carried a three-line paragraph under a three-line heading; the copy
     is now one line over two and both are set large. */
  --dvl-agency-sub: clamp(26px, 4.1667vw, 80px);        /* card line 2  80  */
  --dvl-agency-title: clamp(32px, 3.4896vw, 67px);      /* "Services &" 67  */
  --dvl-agency-body: clamp(16px, 1.875vw, 36px);        /* services copy 36 */
  --dvl-agency-tag: clamp(11px, 0.9375vw, 18px);        /* <DESIGN> etc  18 */
  --dvl-agency-outro: clamp(38px, 6.25vw, 120px);       /* stage 5 head 120 */

  /* The <AGENCY-BOX> caption. Not in the spec sheet - it is the one piece of
     type here without an inspector panel - so it keeps the size it had and
     only picks up the corrected family and token colour. */
  --dvl-agency-box: clamp(10px, 0.72vw, 13px);

  /* ---- rhythm --------------------------------------------------------- */
  /* Floor raised 20 -> 24 so the mobile gutter matches every other page and
     the design's own 24px inset. The vw term and ceiling are untouched. */
  /* 80px at 1920, measured the same way: page3's 1759px text box centred in
     a 1920 frame leaves exactly 80.5 either side, and page1's "Agency" sits
     80px from the left edge. */
  --dvl-agency-pad: clamp(24px, 4.1667vw, 80px);        /* page gutter 80   */
  --dvl-agency-header-clear: 92px;  /* overwritten from the measured header */
  --dvl-agency-footer-h: 96px;      /* overwritten from the measured footer */

  /* Where the lockup parks in stage 2, measured from the top of the stage.
     `auto` means "derive it from the header we actually measured", which is
     what you want in almost every case.

     IF YOU OVERRIDE IT, USE A SINGLE px VALUE. Nothing in this stylesheet
     consumes this property — dvl-agency.js reads it back with
     getComputedStyle().getPropertyValue(), and an unregistered custom
     property is handed back as its unresolved token sequence. A clamp() or a
     calc() therefore arrives as the literal string, parses to its first
     number, and the lockup parks at the clamp's minimum at every viewport
     size while looking perfectly reasonable in the CSS. */
  --dvl-agency-lockup-top: auto;

  /* The whole of stages 1-3, as document height. Reduced from 680vh to 420vh
     for snappy, effortless navigation without exhausting scroll effort. */
  --dvl-agency-stage-h: 420vh;

  /* The distance Services is pulled up into the stage handover (matching cue 8.2). */
  --dvl-agency-services-pull: 58vh;

  /* The sticky act's height. */
  --dvl-agency-act-h: 100vh;

  /* The lens the card's tip-back is seen through. The homepage's value; see
     the note on .dvl-agency__act. */
  --dvl-agency-perspective: 1400px;

  /* ---- and the bits that are not decoration --------------------------- */
  position: relative;
  z-index: 1;                 /* see the note directly below */
  background: var(--dvl-agency-paper);
  color: var(--dvl-agency-ink);
  font-family: var(--dvl-agency-font-display);

  /* THE INTER OPTICAL-SIZING FIX, carried over from the 404 page. Inter is a
     variable font with an `opsz` axis, and browsers apply it automatically
     from the font size - so type set large picks up the display optical
     size, which is noticeably tighter and thinner than what Figma renders.
     Pinning the axis at 14 makes the browser draw the same cut Figma does.
     Without it every large heading on this page is subtly wrong in a way
     that reads as "the font is not quite right" rather than as a bug. */
  font-optical-sizing: none;
  font-variation-settings: "opsz" 14;

  /* overflow-x ONLY. A plain `overflow: clip` here was for the card's old
     flight up past the pin; the card no longer flies, and clipping BOTH axes
     on the ancestor of a sticky element is the classic way to make sticky
     silently stop sticking. `overflow-x: clip` with y left visible is valid
     and creates no scroll container, so the act still sticks. */
  overflow-x: clip;
}

/* WHY THE EXPLICIT z-index ABOVE, on a page with no other canvas.
   dvl-stage.js puts ONE fixed full-viewport canvas on <body> at z-index 0,
   last in the DOM. `z-index: auto` and `z-index: 0` share a stacking layer
   and DOM order breaks the tie, so a full-bleed section with no z-index is
   painted over by that canvas — which has already happened to works, to
   services and to the footer on this site in turn. If the stage block's gate
   is ever widened to include this page, this line is what keeps the agency
   page visible. Costs nothing when it is not. */


/* ==========================================================================
   STAGE 1-3 — THE STICKY ACT
   --------------------------------------------------------------------------
   TWO elements. The STAGE is tall and holds the scroll length; the ACT is one
   viewport, sticks to the top of it, and is what you actually look at.

   WHY STICKY RATHER THAN ScrollTrigger's `pin`, which this was built with
   first. A pin creates a spacer of `element height + pin distance` and, when
   the pin releases, parks the pinned element at the BOTTOM of that spacer —
   so the act still had a full viewport of its own left to scroll past, with
   the card already gone and nothing in it. That was the empty screen between
   the philosophy card and Services, and no amount of re-timing the cue sheet
   could reach it, because it was not in the timeline at all: the timeline had
   already finished. Sticky has no spacer. The act unsticks exactly at the
   stage's bottom edge, and Services begins at that same edge.

   THE HEIGHT BELOW IS THE SEQUENCE'S DURATION, and the only place it is
   written down. The scrub runs from the stage's top hitting the top of the
   viewport to its bottom hitting the bottom, which is (height - 100svh) of
   scroll — so 600svh here means five viewports of animation. dvl-agency.js
   reads none of it; it just scrubs `top top` -> `bottom bottom` on this
   element, which is correct for any value.
   ========================================================================== */
#dvl-agency .dvl-agency__stage {
  position: relative;
  z-index: 1;
  height: 600vh;
  height: var(--dvl-agency-stage-h, 600vh);
}

#dvl-agency .dvl-agency__act {
  position: relative;
  top: 0;
  left: 0;
  width: 100%;

  /* THE CARD'S RECEDE IS A REAL 3D TIP, and a rotateX with no perspective is
     just a vertical squash — the depth is entirely in this one property.
     1400px is the homepage's value (CONFIG.recede.perspective in
     dvl-services.js); lower is a wider lens and a more violent tip.

     It goes on the ACT, the card's parent, exactly as the homepage puts it
     on the section rather than on the plane. Perspective on a parent has its
     vanishing point at the PARENT's centre, so an off-centre child projects
     skewed; the card fills the act exactly, so the two origins coincide and
     this is identical to a per-element transformPerspective — with the
     advantage of matching the homepage's arrangement line for line.

     It is on the sticky element ITSELF, not on an ancestor of it, so it does
     not create a containing block that would stop the act sticking. Moving
     it up to #dvl-agency would. */
  perspective: var(--dvl-agency-perspective, 1400px);
  height: 100vh;
  height: var(--dvl-agency-act-h, 100vh);
  overflow: hidden;
  background: var(--dvl-agency-paper);
  isolation: isolate;   /* keeps the card's z-index local to the act */
}

/* ---- the pink ground ----------------------------------------------------
   Two layers, both behind the mesh: a broad diagonal sweep and a soft bloom
   at the lower left. This is what stage 1 looks like on a machine with no
   WebGL, so it is composed to stand alone.

   ELEMENTOR LAZY-LOADS CONTAINER BACKGROUNDS. The rule
   `.e-con.e-parent:nth-of-type(n+3):not(.e-lazyloaded)` sets
   `background-image: none !important`, and the theme's reset sets
   `background-image: initial !important` on `*`. An ID selector plus
   !important is the only thing that survives both — this has silently eaten
   a gradient on this site before. -------------------------------------- */
#dvl-agency .dvl-agency__wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    radial-gradient(120% 90% at 6% 96%,
                    var(--dvl-agency-blush-deep) 0%,
                    var(--dvl-agency-blush) 26%,
                    var(--dvl-agency-blush-pale) 48%,
                    rgba(255, 255, 255, 0) 72%),
    linear-gradient(118deg,
                    var(--dvl-agency-blush) 0%,
                    var(--dvl-agency-blush-pale) 34%,
                    rgba(255, 255, 255, 0) 58%) !important;
}

/* ---- the prism canvas ---------------------------------------------------
   dvl-agency.js injects the <canvas> in here.

   THE BLUR IS NOW ALMOST NOTHING, and that is the point. The first build
   swept a tube and leaned on a 14px CSS blur to pass it off as satin; this
   is the homepage's actual glass prism, with real transmission, a real
   clearcoat and a PMREM studio behind it, and blurring that away throws out
   the specular sheen that is the whole reason for the swap. 2px takes the
   aliasing off the long edges and stops there.

   Turn it off entirely with --dvl-agency-mesh-blur: 0px. Past about 6px you
   are back to looking at a pink smudge.

   inset is 0, not the old -12%: the overspill existed to give a heavy blur
   somewhere to fade out, and with 2px it would only push the prism off its
   mark. filter still creates a containing block, so the canvas inside is
   positioned against this element rather than against the section. That is
   intentional. */
#dvl-agency .dvl-agency__mesh {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  filter: blur(var(--dvl-agency-mesh-blur, 2px));
}
#dvl-agency .dvl-agency__mesh canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* ---- the lockup: "Agency" and "Shape, Test, Build" ----------------------
   ONE element holds both, because they share a baseline in the comp and
   they travel together in stage 2. Positioned at the hero baseline here;
   the JS measures the distance from here to --dvl-agency-lockup-top and
   tweens `y` by that many pixels, re-measuring on every refresh. Nothing
   about the parked position is hardcoded in this file except the target. */
#dvl-agency .dvl-agency__lockup {
  position: absolute;
  z-index: 3;
  left: var(--dvl-agency-pad);
  right: var(--dvl-agency-pad);
  top: 52%;
  transform: translateY(-50%);
  display: flex;
  align-items: baseline;
  gap: clamp(24px, 6vw, 130px);
  will-change: transform;
}

#dvl-agency .dvl-agency__title {
  flex: 0 0 auto;
  position: relative;
}

/* "Agency" - Inter 400 / 80 / 112% / -6% / Divlab font dark. */
#dvl-agency .dvl-agency__word {
  display: block;
  margin: 0;
  font-size: var(--dvl-agency-display);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.06em;
  color: var(--dvl-agency-ink);
}

/* The <AGENCY-BOX> caption. Mono, uppercase, and tracked out — it is a tag,
   not a subtitle, and it reads as one only at this size with this much
   letter-spacing. */
#dvl-agency .dvl-agency__box {
  display: block;
  margin-top: 0.55em;
  font-family: var(--dvl-agency-font-mono);
  font-size: var(--dvl-agency-box);
  font-weight: 400;
  letter-spacing: 0.16em;
  color: var(--dvl-agency-mute);
  text-transform: uppercase;
}

/* "Shape, Test, Build" is monospaced in the comp — the commas sit in their
   own cells and that even rhythm is the point of the line. The first word is
   muted and the rest is ink, which is the only emphasis on the page. */
/* THE TAGLINE IS NOT ON SCREEN AT 0% SCROLL.
   The reference prototype opens on "Agency" and the prism alone; the phrase
   rises into place on the first scroll, alongside the prism's turn. The
   start state is therefore HIDDEN, and hidden two ways:

     opacity: 0     so there is nothing to see
     visibility     so there is nothing to hit-test, nothing to tab to, and
                    nothing for a screen reader to announce out of order

   Opacity alone is what the first build shipped, and it is the thing that
   reads as "pre-faded": an invisible phrase still occupies its box, still
   takes the pointer, and still sits in the accessibility tree as though it
   were on screen. GSAP's `autoAlpha` drives both properties together, which
   is why the timeline uses it rather than `opacity`.

   UNDER --ready ONLY, as with every other hidden start state in this file:
   no JS means no timeline to reveal it, so without that class the phrase is
   simply visible. Same for reduced motion and for mobile, both further
   down — a phrase that never appears is a content bug, not a degraded
   animation. */
#dvl-agency.dvl-agency--ready .dvl-agency__tagline {
  opacity: 0;
  visibility: hidden;
}

#dvl-agency .dvl-agency__tagline {
  flex: 1 1 auto;
  margin: 0;
  text-align: center;
  will-change: transform, opacity;
  /* IBM Plex Mono 400 / 100 / 112% / -10%, filled with the gradient below. */
  font-family: var(--dvl-agency-font-mono);
  font-size: var(--dvl-agency-tagline);
  font-weight: 400;
  line-height: 1.12;
  letter-spacing: -0.1em;
  color: var(--dvl-agency-ink);
  white-space: nowrap;
}
/* ==========================================================================
   THE GRADIENT ON "Shape, Test, Build"
   --------------------------------------------------------------------------
   Not a flat colour: the phrase carries the same ambient falloff the hero
   and the footer use on the homepage, so the light appears to arrive from
   the same place on every page. The two stops are the design's own tokens,
   named here as Figma names them.

   THE STOP ORDER IS LIGHT -> DARK, which is the reverse of the order the
   Figma inspector prints. That is deliberate, and it is what the comp shows:
   "Shape," is the lit end and "...Build" falls off into the ink. An
   inspector lists a gradient's stops along its own vector, and this one's
   handle runs from the dark end toward the light one — so reading the panel
   top to bottom and pasting it gives a phrase that is dark at the start and
   pale at the end, i.e. backwards, while still looking like a perfectly
   plausible gradient in a screenshot. To take the inspector literally
   instead, swap the two var() names below and change nothing else.

   THE FALLBACK MATTERS. background-clip: text over a transparent fill is an
   INVISIBLE headline anywhere the background does not paint — print,
   forced-colours mode, an old browser. `color` is therefore set first, and
   to a real value, so those cases get solid ink instead of a blank line;
   -webkit-text-fill-color only wins where the gradient actually renders.

   IT IS ON AN INNER <span>, NOT ON THE <p>. The gradient box is the
   element's own box, so on a full-width block the falloff is measured across
   the whole column and the phrase shows only a narrow slice of it —
   shrink-wrapping the box to the text is what makes 0% and 100% mean "Shape"
   and "Build". And it cannot be the <p>, because that is a flex item in the
   lockup: flex blockifies its children, so `display: inline-block` there is
   simply discarded and the box goes full width again with no warning. The
   <p> stays the flex item and centres; the span inside it carries the
   gradient.
   ========================================================================== */
#dvl-agency .dvl-agency__headline--gradient {
  display: inline-block;
  color: var(--dvl-color-font-dark);
  background: linear-gradient(135deg,
              var(--dvl-color-font-light) 0%,
              var(--dvl-color-font-dark) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* On desktop the three words and the two separators are all plain inline
   text inside one gradient box, so the phrase is a single run and the
   falloff crosses all of it. Mobile takes them apart - see the mobile
   block at the end of this file. */
#dvl-agency .dvl-agency__w,
#dvl-agency .dvl-agency__sep {
  display: inline;
}

/* Forced-colours mode throws the background away and keeps the fill, which
   would leave the phrase invisible. Hand it its colour back. */
@media (forced-colors: active) {
  #dvl-agency .dvl-agency__headline--gradient {
    background: none;
    -webkit-text-fill-color: currentColor;
  }
}

/* ---- the grey card ------------------------------------------------------
   Full viewport, parked below the fold. Stage 2 brings it up to a band across
   the bottom; stage 3 seats it, holds it long enough to read, then recedes
   it into the page.

   transform-origin is 50% 20%, copied from .dvl-services__plane on the
   homepage. Tipping about a line near the top sends the far edge away and
   brings the near edge down toward the reader, which is what makes the card
   read as an object lying back; about the centre it merely leans, and about
   the top edge it hinges like a lid.

   THERE IS NO preserve-3d HERE, and the comment that used to sit in this
   spot claiming it was required was simply wrong. It said the text would
   stop foreshortening without it. Flattening the children into the card's
   own plane is EXACTLY what is wanted: the card renders flat, and then the
   whole rendered result is tipped, so the type foreshortens with it.
   `transform-style: preserve-3d` only matters when children carry 3D
   transforms of their own, which none of these do.

   It was also actively harmful. preserve-3d puts every child into a 3D
   rendering context where each composited layer is rasterised separately,
   every frame, while the card tips — and a line would drop out for a frame
   or two mid-scroll and come back. The computed clip-path, opacity and box
   of that line were all correct the whole time, which is how you can tell a
   paint bug from a timeline bug: the state is right and the pixels are
   missing. backface-visibility went with it, for the same reason — it is
   another layer-promotion hint buying nothing on a card that never turns
   away from the viewer.

   The corner radius arrives with the lift: a full-bleed rectangle has no
   corners to round, and the moment it has margins it should look like a
   card. */
#dvl-agency .dvl-agency__card {
  position: absolute;
  z-index: 2;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* The comp leaves a long, deliberate silence between the heading and the
     line under it — about a fifth of the screen — and that gap is doing the
     work a paragraph break cannot at this size. */
  gap: clamp(40px, 12vh, 165px);
  padding: calc(var(--dvl-agency-header-clear) + 4vh)
           var(--dvl-agency-pad) 6vh;
  background: var(--dvl-agency-card);
  transform: translate3d(0, 100%, 0);
  transform-origin: 50% 20%;
  border-radius: 0;
  box-shadow: none;
  will-change: transform;
}

/* No-JS: the card must not sit over the hero as an opaque slab. Without
   `.dvl-agency--ready` it is returned to flow-like visibility at the bottom
   of the act, which is where a reader who never got the timeline should
   find it. */
#dvl-agency:not(.dvl-agency--ready) .dvl-agency__card {
  position: relative;
  inset: auto;
  transform: none;
  min-height: 100svh;
}

#dvl-agency .dvl-agency__lede,
#dvl-agency .dvl-agency__sub {
  margin: 0;
  text-align: center;
}
/* "DivLab Begins With The <Div>" - 80 / lh 100 / -2%. Figma reports the
   family as "Mixed" because the <Div> run is mono and the rest is Inter;
   that split is the <code> element in the markup, not a second style here.
   lh 100 on 80 is 1.25. */
#dvl-agency .dvl-agency__lede {
  font-size: var(--dvl-agency-lede);
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--dvl-agency-ink);
}
/* "A Simple, Empty Element…" - Inter LIGHT 300 / 80 / lh 100 / -2%. Same
   size as the headline above it; the weight is the only thing separating
   them, which is why 300 here is not a detail to round off. */
#dvl-agency .dvl-agency__sub {
  font-size: var(--dvl-agency-sub);
  font-weight: 300;
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--dvl-agency-ink);
}

/* ---- the <Div> token ----------------------------------------------------
   Monospaced, as the comp sets it: it is a literal element name quoted in a
   sentence, and the mono face is what says so. Sized in em so it tracks the
   headline's clamp, and nudged down slightly because a mono face at the same
   px reads larger than the grotesk beside it.

   The browser default for <code> is a monospace family AND, in some themes,
   a background and a border. Both are cleared here — Hello theme is quiet
   about it but a plugin stylesheet may not be. */
#dvl-agency .dvl-agency__code {
  font-family: var(--dvl-agency-font-mono);
  font-size: 0.94em;
  letter-spacing: -0.02em;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
}

/* ---- the line mask ------------------------------------------------------
   THE REVEAL IS FROM EACH LINE'S OWN VERTICAL CENTRE, not from below. The
   line is clipped to a zero-height band across its middle and the band opens
   outward, so the type appears to be developing in place rather than sliding
   up into view — which is what the comp shows and what the rest of this
   site's sections deliberately do NOT do.

   `display: block` on the span matters: clip-path on an inline box clips to
   the line box, which for a wrapped line is not the rectangle you think it
   is. Each line is its own block, and the line breaks are authored in the
   markup rather than left to wrapping, so the three lines in the comp are
   three lines at every width above the mobile breakpoint.

   The start state is under --ready only. Without the module every line is
   simply visible. */
/* NO `will-change` HERE. It was `clip-path, opacity`, and it is the other
   half of the dropped-line bug above: will-change promotes each line to its
   own composited layer, and a handful of separately-rasterised layers inside
   a card that is being tipped every frame is exactly the situation a
   compositor drops one of. The reveal is three short clip-path tweens on
   three elements - there is nothing here that needs the hint, and the cost
   of being wrong about it is a line of the headline blinking out. */
#dvl-agency .dvl-agency__ln {
  display: block;
}
#dvl-agency.dvl-agency--ready .dvl-agency__ln {
  clip-path: inset(50% 0 50% 0);
  opacity: 0;
}

/* THE WRAPPER maskLines() BUILDS, and above the breakpoint it does nothing
   but sit there. It is block so that the mobile rules below have a box with
   a height to translate by; here that box is exactly the line's own box, it
   carries no styles of its own, and the clip above still applies to the
   line around it. Desktop line rectangles measure identical with and
   without it. */
#dvl-agency .dvl-agency__lni {
  display: block;
}


/* ==========================================================================
   STAGE 4 — SERVICES & COLLABORATION
   --------------------------------------------------------------------------
   Two columns: the heading is vertically centred against the list, which is
   why it is its own grid cell with `align-self: center` rather than sitting
   at the top of the column. At the comp's proportions the heading lands
   beside the third item, and that only stays true if the column centres.
   ========================================================================== */
/* ==========================================================================
   SERVICES, AND THE HANDOVER OUT OF THE STICKY STAGE
   --------------------------------------------------------------------------
   THE NEGATIVE TOP MARGIN IS THE FIX FOR THE EMPTY SCREEN, and it is worth
   understanding what it is undoing.

   A sticky element always leaves ITS OWN HEIGHT of scroll behind it: the act
   unsticks at the stage's bottom edge and then has a full viewport left to
   scroll past. For the whole of that viewport the timeline is already over
   and the act has nothing in it, so the visitor scrolls a screen of blank
   paper. Putting Services immediately after the stage does NOT reach this
   - measured, the seam between them was exactly 0px and the blank screen was
   still there, because the blank was INSIDE the act, not between the two.

   Pulling Services up by one act height puts it on exactly the same run of
   scroll as that scroll-out, where it paints over it (z-index 2 against the
   stage's 1). So the last stretch of the sequence is Services climbing the
   screen at normal scroll speed, with the receded card still visible above
   it, instead of a screen of nothing. It also takes a viewport of dead
   length out of the document.

   IT MUST EQUAL THE ACT'S HEIGHT EXACTLY. Both are 100svh. If the act ever
   stops being a full viewport, this has to follow it or Services either
   uncovers a strip of blank act or starts climbing over the card too early.

   THE CUE SHEET DEPENDS ON THIS. dvl-agency.js finishes the card's whole
   gesture by cue 8.0 precisely so that it is over before Services begins to
   rise; move this margin and that number has to move with it.
   ========================================================================== */
#dvl-agency .dvl-agency__services {
  position: relative;
  z-index: 2;
  margin-top: calc(-1 * var(--dvl-agency-services-pull, 58vh));
  background: var(--dvl-agency-paper);
  /* The bottom pad is deliberately much larger than the top one: above it
     there is a section arriving, below it the page changes colour entirely.
     A dark band starting a short distance under the last line reads as a
     crop rather than as a new chapter. */
  padding: clamp(80px, 14vh, 190px) var(--dvl-agency-pad)
           clamp(150px, 27vh, 360px);
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.25fr);
  gap: clamp(40px, 7vw, 120px);
  align-items: center;
}

#dvl-agency .dvl-agency__services-head {
  align-self: center;
}
/* "Services & Collaboration" - Inter 400 / 67 / auto / -10%. `normal`, not
   a number: Figma says Auto, which is the font's own default line box. */
#dvl-agency .dvl-agency__services-head .dvl-agency__word {
  font-size: var(--dvl-agency-title);
  line-height: normal;
  letter-spacing: -0.1em;
}

#dvl-agency .dvl-agency__list {
  display: flex;
  flex-direction: column;
  gap: clamp(26px, 3.4vh, 52px);
  margin: 0;
  padding: 0;
  list-style: none;
}

#dvl-agency .dvl-agency__item {
  will-change: transform, opacity;
}
#dvl-agency.dvl-agency--ready .dvl-agency__item {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
}

/* The tag is a <span> inside the item rather than a <dt>/<dd> pair: it is a
   label for the sentence below it and nothing else consumes it as data. It
   reuses .dvl-agency__box's look at a slightly tighter track. */
/* <DESIGN> etc - IBM Plex Mono MEDIUM 500 / 18 / auto / -2% / font light.
   Note the tracking is NEGATIVE. These were tracked out at +0.1em when they
   were 13px labels; at 18px the comp sets them tight, and keeping the old
   positive tracking at the new size makes them read as a different element
   entirely. */
#dvl-agency .dvl-agency__tag {
  display: block;
  margin-bottom: 0.7em;
  font-family: var(--dvl-agency-font-mono);
  font-size: var(--dvl-agency-tag);
  font-weight: 500;
  line-height: normal;
  letter-spacing: -0.02em;
  color: var(--dvl-agency-mute);
  text-transform: uppercase;
}

/* Service paragraphs - Inter 400 / 36 / lh 50 / -6%. 50 on 36 is 1.3889. */
#dvl-agency .dvl-agency__copy {
  margin: 0;
  font-size: var(--dvl-agency-body);
  font-weight: 400;
  line-height: 1.3889;
  letter-spacing: -0.06em;
  color: var(--dvl-agency-ink);
  max-width: 46ch;
}


/* ==========================================================================
   STAGE 5 — THE OUTRO
   --------------------------------------------------------------------------
   Dark ground, the vector grid, one headline. It is a full viewport so the
   inversion has something to be true of: a short dark strip would flip the
   header twice in a thumb's worth of scroll.

   The grid is drawn in CSS, not as an SVG file — it is four repeating
   linear-gradients (two orthogonal, two diagonal) and therefore resolution
   independent, uploadable-free, and one variable (--dvl-agency-grid-step)
   away from a different density. The diagonals are what make it read as the
   comp's drafting grid rather than as graph paper.

   ID selector + !important again: same Elementor lazy-load rule as the wash.
   ========================================================================== */
#dvl-agency .dvl-agency__outro {
  position: relative;
  z-index: 2;
  min-height: 100vh !important;
  min-height: 100dvh !important;
  box-sizing: border-box !important;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--dvl-agency-header-clear, 92px) + 2vh)
           var(--dvl-agency-pad)
           calc(var(--dvl-agency-footer-h, 96px) + clamp(20px, 4vh, 60px)) !important;
  background: var(--dvl-agency-dark);
  color: var(--dvl-agency-dark-ink);
}

#dvl-agency .dvl-agency__grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(0deg,
      var(--dvl-agency-grid) 0 1px,
      transparent 1px var(--dvl-agency-grid-step)),
    repeating-linear-gradient(90deg,
      var(--dvl-agency-grid) 0 1px,
      transparent 1px var(--dvl-agency-grid-step)),
    repeating-linear-gradient(45deg,
      var(--dvl-agency-grid) 0 1px,
      transparent 1px calc(var(--dvl-agency-grid-step) * 1.414)),
    repeating-linear-gradient(-45deg,
      var(--dvl-agency-grid) 0 1px,
      transparent 1px calc(var(--dvl-agency-grid-step) * 1.414)) !important;
}

/* "Turning scattered ideas into clear digital systems"
   Inter LIGHT 300 / 120 / lh 110 / -6% / solid #FFFFFF. 110 on 120 is
   0.9167 - a line height BELOW 1, which is what gives the two lines their
   tight stack in the comp.

   max-width in em, not ch: the comp breaks it "…ideas into / clear digital
   systems" and its text box measures ~1375px at 120px type, which is 11.5em.
   Expressed that way the break survives every viewport instead of only the
   one it was measured at. */
#dvl-agency .dvl-agency__outro-head {
  position: relative;
  z-index: 1;
  margin: 0;
  max-width: 11.5em;
  text-align: center;
  font-size: var(--dvl-agency-outro);
  font-weight: 300;
  line-height: 0.9167;
  letter-spacing: -0.06em;
  color: var(--dvl-agency-outro-ink, #FFFFFF);
}


/* ==========================================================================
   TABLET — 1024 and below (Elementor's own breakpoint, as everywhere else)
   ========================================================================== */
@media (max-width: 1024px) {
  #dvl-agency .dvl-agency__lockup {
    flex-direction: column;
    align-items: flex-start;
    gap: clamp(18px, 4vh, 40px);
  }
  #dvl-agency .dvl-agency__tagline {
    text-align: left;
    white-space: normal;
  }
  #dvl-agency .dvl-agency__services {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(34px, 6vh, 64px);
    align-items: start;
  }
  #dvl-agency .dvl-agency__services-head { align-self: start; }
}


/* ==========================================================================
   MOBILE — 767 and below
   --------------------------------------------------------------------------
   THE PIN GOES AWAY, and that is a decision rather than a fallback. The act
   is roughly three-and-a-half viewports of scroll during which the page
   advances nothing but one animation; with a wheel that is a set piece,
   under a thumb it reads as stuck — the same call dvl-services.js makes for
   its own pinned column. dvl-agency.js reads this breakpoint from
   CONFIG.breakpoint and lets the three panels stack, each revealing on its
   own trigger.

   The authored line breaks in the headline are unwrapped here: three lines
   set for a 1440 canvas become six ragged ones at 390px.
   ========================================================================== */
/* ==========================================================================
   TABLET (768px - 1366px)
   -------------------------------------------------------------------------- */
@media (max-width: 1366px) and (min-width: 768px) {
  #dvl-agency {
    --dvl-agency-stage-h: 380vh;
    --dvl-agency-services-pull: 50vh;
  }
}

@media (max-width: 767px) {

  /* ======================================================================
     MOBILE - and it is the SAME MACHINERY as desktop, not a stacked
     fallback any more.
     ----------------------------------------------------------------------
     This block used to switch the whole sequence off: no sticky, no scroll
     length, no WebGL, three panels simply stacked. The storyboard asks for
     the opposite - the hero pins on a phone too, the prism turns with the
     scroll, and three words take turns at the bottom right - so the stage,
     the sticky act, the card and the whole scrubbed timeline are all kept.
     What changes here is TYPE, PLACEMENT and DURATION, not structure.

     THE MOBILE FRAME IS 402px WIDE with 24px gutters, derived the same way
     the desktop frame was: the service-paragraph box reads 354px, and
     402 - 24 - 24 = 354 exactly. Sizes below are
     clamp(floor, figma-px / 4.02 vw, figma-px), so a 402px-wide phone
     matches the comp exactly and narrower ones scale down.
     ====================================================================== */

  #dvl-agency {
    --dvl-agency-pad: 24px;                              /* mobile gutter   */

    --dvl-agency-display: clamp(28px, 8.955vw, 36px);    /* "Agency"     36 */
    --dvl-agency-tagline: clamp(36px, 11.94vw, 48px);    /* Shape/Test   48 */
    --dvl-agency-lede: clamp(28px, 8.955vw, 36px);       /* DivLab…      36 */
    --dvl-agency-sub: clamp(28px, 8.955vw, 36px);        /* A Simple…    36 */
    --dvl-agency-title: clamp(28px, 8.955vw, 36px);      /* "Services &" 36 */
    --dvl-agency-body: clamp(19px, 5.97vw, 24px);        /* service copy 24 */
    --dvl-agency-tag: clamp(12px, 3.483vw, 14px);        /* <DESIGN>     14 */
    --dvl-agency-box: clamp(12px, 3.483vw, 14px);        /* <AGENCY-BOX> 14 */
    --dvl-agency-outro: clamp(38px, 11.94vw, 48px);      /* closing head 48 */

    /* TALLER than desktop's 680svh, which is the opposite of what it was.
       560svh looked generous on paper - 4.6 viewports - but the three word
       beats only occupied a quarter of it, which came to 389px each on a
       875px phone. A single iOS flick carries 1000-2000px, so one gesture
       consumed the entire sequence and it read as the page lurching rather
       than as three beats.

       820svh with the re-weighted cue sheet gives each beat about 1200px -
       roughly one unhurried swipe - while leaving the card section the same
       absolute runway it already had. A phone needs MORE scroll per beat
       than a wheel, not less, because momentum means the finger is not
       metering the distance. */
    /* THE TRACK FOR THE SCRUBBED MOBILE MODES ONLY. In the shipped 'flow'
       mode the block at the bottom of this file overrides the stage to
       height:auto and none of this applies.

       It is short because the words are not on it: 820svh when three word
       beats each had to own about a swipe, 500svh once the track only had
       to carry the card sequence. Kept accurate rather than deleted,
       because CONFIG.mobileMode can still select those modes. */
    --dvl-agency-stage-h: 220vh;
    --dvl-agency-services-pull: 24vh;
  }

  /* ---- type ----------------------------------------------------------
     Figma gives line height in px against a px size, so these are ratios:
     50/36 = 1.3889, 50/24 = 2.0833, 50/48 = 1.0417. The 24px paragraph at
     50px leading is deliberately airy - that is what the comp shows. */
  #dvl-agency .dvl-agency__word { line-height: 1.12; letter-spacing: -0.04em; }
  #dvl-agency .dvl-agency__tagline { line-height: 1.12; letter-spacing: -0.04em; }
  #dvl-agency .dvl-agency__lede { line-height: 1.3889; letter-spacing: -0.04em; }
  #dvl-agency .dvl-agency__sub { line-height: 1.3889; letter-spacing: -0.04em; }
  #dvl-agency .dvl-agency__services-head .dvl-agency__word {
    line-height: 1.12;
    letter-spacing: -0.04em;
  }
  #dvl-agency .dvl-agency__box,
  #dvl-agency .dvl-agency__tag {
    font-weight: 400;            /* Regular on mobile, Medium on desktop */
    line-height: 1.12;
    letter-spacing: -0.06em;
  }
  #dvl-agency .dvl-agency__copy {
    line-height: 2.0833;
    letter-spacing: -0.04em;
    max-width: none;
  }
  #dvl-agency .dvl-agency__outro {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
  }
  #dvl-agency .dvl-agency__outro-head {
    line-height: 1.0417;
    letter-spacing: -0.06em;
    text-align: center;
    max-width: min(14em, 100%);
    margin: 0 auto;
  }

  /* ---- the hero: "Agency" left, one word at the bottom right ----------
     The lockup stops being a flex row and becomes a positioning frame; the
     two halves are placed independently because the comp puts them on
     opposite corners rather than on one baseline. */
  #dvl-agency .dvl-agency__lockup {
    display: block;
    top: 0;
    left: var(--dvl-agency-pad);
    right: var(--dvl-agency-pad);
    bottom: 0;
    transform: none;
    gap: 0;
  }
  #dvl-agency .dvl-agency__title {
    position: absolute;
    top: 34%;
    left: 0;
  }

  /* ---- the three words ------------------------------------------------
     THIS IS WHERE THE ONE PIECE OF MARKUP BECOMES SOMETHING ELSE. The
     separators go, and the three words are stacked in the same place so the
     timeline can cross-fade between them: each is absolute, right-aligned,
     sharing one origin. The parent keeps a line box of its own height so it
     occupies space even though every child is out of flow.

     Each word carries its OWN gradient here. On desktop the falloff runs
     across the whole phrase; on mobile only one word is ever visible, so a
     shared box would show each of them a third of the ramp and they would
     not match. */
  #dvl-agency .dvl-agency__tagline {
    position: absolute;
    top: 70%;
    left: 0;
    right: 0;
    text-align: right;
    white-space: nowrap;
  }
  #dvl-agency .dvl-agency__sep { display: none; }

  #dvl-agency .dvl-agency__headline--gradient {
    display: block;
    position: relative;
    height: 1.12em;
    background: none;
    -webkit-text-fill-color: currentColor;
  }
  #dvl-agency .dvl-agency__w {
    display: block;
    position: absolute;
    top: 0;
    right: 0;
    white-space: nowrap;
    color: var(--dvl-color-font-dark);
    background: linear-gradient(135deg,
                var(--dvl-color-font-light) 0%,
                var(--dvl-color-font-dark) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
  }
  @media (forced-colors: active) {
    #dvl-agency .dvl-agency__w {
      background: none;
      -webkit-text-fill-color: currentColor;
    }
  }

  /* The whole tagline is no longer hidden as one - the timeline hides and
     shows the individual words - so the desktop start state is released
     and re-expressed on .dvl-agency__w below. */
  #dvl-agency.dvl-agency--ready .dvl-agency__tagline {
    opacity: 1;
    visibility: visible;
  }
  #dvl-agency.dvl-agency--ready .dvl-agency__w {
    opacity: 0;
    visibility: hidden;
  }

  /* ---- the card -------------------------------------------------------
     Still the full-viewport overlay it is on desktop - the storyboard shows
     the pale slate rising over the hero exactly as it does there - so the
     only changes are the ones a small screen needs: left-aligned copy, real
     line breaks released, and no 3D tip (a 402px-wide card tipping 52
     degrees is mostly edge). */
  /* ---- THE STATIC PRISM ----------------------------------------------
     No WebGL on the phone (CONFIG.tile.mobile3D = false). This is a flat
     render of the SAME prism - same geometry, same studio, same glass -
     with the page's own wash behind it as the refraction backdrop, so the
     pink in it is real transmitted colour rather than a tint. That is why
     the wash is switched off here: it is already IN the image, and
     doubling it would darken the left edge.

     The URL is relative to THIS STYLESHEET, so the one path is correct on
     the test page and in the theme (both have assets/ beside the css).

     background-size: cover with a top-left origin, because the prism is
     composed into the upper-left of the frame and the bottom is empty
     wash - cropping from the bottom loses nothing.

     It still fades out on meshLift like the canvas did: the timeline
     tweens this element's opacity, not the renderer's. Nothing else moves,
     which is the point - the words carry the motion. */
  #dvl-agency .dvl-agency__mesh {
    filter: none;
    background-image: none !important;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    height: 100vh;
    height: 100lvh;
    z-index: 1;
  }
  #dvl-agency .dvl-agency__wash {
    display: block !important;
    z-index: 0;
  }
  #dvl-agency .dvl-agency__lockup {
    z-index: 2;
  }

  /* ---- the card is an overlay here too --------------------------------
     Full viewport, parked below the fold, brought up by the scrubbed
     timeline exactly as on desktop. Only its internals change for a narrow
     column: left-aligned, tighter padding, and no shadow or rounding -
     both of those belong to the 3D recede. */
  #dvl-agency .dvl-agency__card {
    justify-content: center;
    align-items: center;
    text-align: center;
    gap: clamp(28px, 7vh, 64px);
    padding: calc(var(--dvl-agency-header-clear) + 3vh)
             var(--dvl-agency-pad) 8vh;
    box-shadow: none;
    border-radius: 0;
    will-change: transform;
  }

  #dvl-agency .dvl-agency__lede,
  #dvl-agency .dvl-agency__sub {
    width: 100%;
    max-width: 800px;
    margin-left: auto;
    margin-right: auto;
    text-align: center;
  }

  #dvl-agency .dvl-agency__ln,
  #dvl-agency .dvl-agency__lni {
    text-align: center;
  }

  /* ---- THE LINE IS A MASK HERE, NOT A CLIP ---------------------------
     The authored desktop line cuts are wrong at 402px, so the copy wraps
     to the device. The spans stay block-level, but for a different reason
     than they do on desktop: up there the block box is what clip-path
     clips, here it is the window the wrapper slides up into.

     WHY THE SWITCH. clip-path on a text box is re-rasterised against the
     glyph bounds on the CPU every time the value changes, and under a
     scrub that is every scroll tick. On iOS Safari it was the last
     CPU-side paint left in the mobile sequence and it landed on the
     busiest stretch of it. A mask plus a translate is two compositor
     properties and no repaint at all. See CONFIG.revealMobile.

     THE HEADROOM IS FOR DESCENDERS. overflow:hidden clips to the padding
     box, and a tight line box cuts the tails off g, p and y - both lines
     of this copy have them. The padding gives the clip somewhere to spill
     and the matching negative margin takes the space back, so the rhythm
     between the lines is unchanged. CONFIG.revealMobile.lift is the other
     half: the start state translates 100% PLUS a few px, so the top of the
     line does not peek into that strip before the reveal begins. */
  #dvl-agency .dvl-agency__ln {
    display: block;
    overflow: hidden;
    padding-bottom: 0.18em;
    margin-bottom: -0.18em;
  }

  /* will-change IS CORRECT HERE, and it is the opposite of the call made
     for the desktop lines a few hundred lines up - so the difference is
     worth stating. Up there the hint was `clip-path, opacity`, it promoted
     three separately-rasterised layers inside a card being tipped in 3D
     every frame, and the compositor dropped one of them: a line of the
     headline blinking out mid-scroll.

     Here there is no 3D on the act at all (perspective: none), the
     promoted properties are transform and opacity rather than clip-path,
     and a layer that only translates never needs re-rasterising. That is
     precisely the case will-change is for. */
  #dvl-agency .dvl-agency__lni {
    will-change: transform, opacity;
  }
  #dvl-agency.dvl-agency--ready .dvl-agency__lni {
    opacity: 0;
  }
  /* The line itself is never clipped or faded here - the wrapper inside it
     carries the whole reveal, and the line only has to stay a window. */
  #dvl-agency.dvl-agency--ready .dvl-agency__ln {
    clip-path: none;
    opacity: 1;
  }

  /* ---- services -------------------------------------------------------
     One column, and the tag sits UNDER its paragraph on mobile, which is
     what the comp shows - so the order is flipped rather than restyled. */
  #dvl-agency .dvl-agency__services {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(40px, 7vh, 72px);
    /* Clear of the floating header, measured rather than guessed. This only
       saves the FIRST line though - everything after it still scrolls
       behind. The bar below is what actually fixes the collision. */
    padding: calc(var(--dvl-agency-header-clear) + clamp(32px, 6vh, 72px))
             var(--dvl-agency-pad)
             clamp(96px, 18vh, 200px);
  }
  #dvl-agency .dvl-agency__list {
    gap: clamp(34px, 5vh, 56px);
  }
  #dvl-agency .dvl-agency__item {
    display: flex;
    flex-direction: column;
  }
  #dvl-agency .dvl-agency__tag {
    order: 2;
    margin-top: 0.2em;
    margin-bottom: 0;
  }
  #dvl-agency .dvl-agency__copy { order: 1; }
}





/* ==========================================================================
   MOBILE, CONFIG.mobileMode === 'flow'   -   THE SHIPPED MOBILE LAYOUT
   --------------------------------------------------------------------------
   No sticky, no scroll track, no scrub. The hero is one ordinary screen,
   the card is the next section under it, and the page scrolls past both
   natively. dvl-agency.js adds .dvl-agency--flow only in this mode, so the
   sticky mobile build in the block above stays intact and reachable behind
   CONFIG.mobileMode.

   IT LIVES OUTSIDE THAT BLOCK, AND AFTER IT, ON PURPOSE. Several of these
   rules tie with one up there on specificity - both are an id plus a class
   plus a class - so source order is what decides them, and a later block is
   the only thing that reliably wins without an !important arms race.

   THE ::before IS THE STRUCTURAL TRICK, and it is worth understanding
   before editing anything here. The card is a CHILD of the act, because on
   desktop it is an overlay that rises over the pinned hero. Put that same
   tree in normal flow with a fixed-height act and the act traps the card
   inside itself and clips it - measured, in an early attempt, the card
   landed at document y=24, inside the hero, behind the prism.

   CSS cannot reparent. So the act goes height:auto and a pseudo-element
   contributes exactly one screen of flow: the prism and the lockup are
   positioned absolutely over that band, and the card simply follows it as
   the next thing in the document. Same markup, same desktop behaviour,
   and the phone gets hero-then-card without a wrapper div.
   ========================================================================== */
@media (max-width: 767px) {

  /* No track. The stage collapses onto its content and the document is
     exactly as long as the sections in it. */
  #dvl-agency.dvl-agency--flow .dvl-agency__stage {
    height: auto;
  }

  /* The act stops being a viewport-sized window and becomes an ordinary
     block that grows. overflow returns to visible because there is no
     fixed height left to clip against, and `top` is released because a
     non-sticky element with top:0 and position:relative would shift. */
  #dvl-agency.dvl-agency--flow .dvl-agency__act {
    position: relative;
    top: auto;
    height: auto;
    overflow: visible;
    perspective: none;
  }
  #dvl-agency.dvl-agency--flow .dvl-agency__act::before {
    content: "";
    display: block;
    height: 100vh;
    height: 100svh;
  }

  /* Both hero layers are pinned to that band rather than to the act, which
     is now several screens tall. The mesh already sets top/height for its
     own reasons in the block above; the lockup is stretched top-to-bottom
     up there and has to be cut back to one screen here or it would run the
     whole length of the card. */
  #dvl-agency.dvl-agency--flow .dvl-agency__lockup {
    position: absolute;
    top: 0;
    bottom: auto;
    height: 100vh;
    height: 100svh;
  }

  /* ---- and the card returns to the flow ------------------------------
     It is a full-viewport overlay everywhere else because it rises over a
     pinned hero. With nothing pinned there is nothing to rise over, so it
     is simply the next section, arriving when you reach it. The opacity is
     the start state for buildFlow()'s one-shot entrance; the transform
     that parked it a screen below is released, because here it has a
     position of its own in the document. */
  #dvl-agency.dvl-agency--flow .dvl-agency__card,
  #dvl-agency.dvl-agency--ready.dvl-agency--flow .dvl-agency__card {
    position: relative;
    inset: auto;
    transform: none;
    min-height: 100vh;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
  }
  #dvl-agency.dvl-agency--ready.dvl-agency--flow .dvl-agency__card {
    opacity: 0;
  }

  /* The pull-up exists to cancel the sticky act's scroll-out. There is no
     scroll-out to cancel here, so leaving it would eat a screen of the
     card - see the long note on .dvl-agency__services. */
  #dvl-agency.dvl-agency--flow .dvl-agency__services {
    margin-top: 0;
  }
}

/* ==========================================================================
   LANDSCAPE MOBILE (iPhone 15/16 Pro landscape, height <= 550px)
   -------------------------------------------------------------------------- */
@media (orientation: landscape) and (max-height: 550px),
       (orientation: landscape) and (max-width: 1024px) and (max-height: 600px) {
  #dvl-agency {
    --dvl-agency-stage-h: 260vh !important;
    --dvl-agency-services-pull: 29vh !important;
  }

  /* Hero Lockup & Tagline - Ensure words ('Shape', 'Test', 'Build') are 100% visible */
  #dvl-agency .dvl-agency__lockup {
    position: absolute !important;
    top: 0 !important;
    bottom: 0 !important;
    left: max(var(--dvl-agency-pad), env(safe-area-inset-left)) !important;
    right: max(var(--dvl-agency-pad), env(safe-area-inset-right)) !important;
    height: 100vh !important;
    height: 100svh !important;
    transform: none !important;
    display: block !important;
    z-index: 2 !important;
  }

  #dvl-agency .dvl-agency__title {
    position: absolute !important;
    top: 50% !important;
    transform: translateY(-50%) !important;
    left: 0 !important;
  }

  #dvl-agency .dvl-agency__word {
    font-size: clamp(26px, 7.5vh, 40px) !important;
    line-height: 1.12 !important;
  }

  #dvl-agency .dvl-agency__box {
    font-size: clamp(9px, 2.5vh, 12px) !important;
    margin-top: 0.3em !important;
  }

  #dvl-agency .dvl-agency__tagline {
    position: absolute !important;
    top: auto !important;
    bottom: max(22px, calc(env(safe-area-inset-bottom, 14px) + 10px)) !important;
    right: 0 !important;
    left: auto !important;
    text-align: right !important;
    font-size: clamp(20px, 6.5vh, 28px) !important;
    line-height: 1.1 !important;
    white-space: nowrap !important;
    opacity: 1 !important;
    visibility: visible !important;
  }

  #dvl-agency .dvl-agency__sep {
    display: none !important;
  }

  #dvl-agency .dvl-agency__headline--gradient {
    display: block !important;
    position: relative !important;
    height: 1.15em !important;
    background: none !important;
    -webkit-text-fill-color: currentColor !important;
  }

  #dvl-agency .dvl-agency__w {
    display: block !important;
    position: absolute !important;
    top: 0 !important;
    right: 0 !important;
    white-space: nowrap !important;
    color: var(--dvl-color-font-dark) !important;
    background: linear-gradient(135deg,
                var(--dvl-color-font-light) 0%,
                var(--dvl-color-font-dark) 100%) !important;
    -webkit-background-clip: text !important;
    background-clip: text !important;
    -webkit-text-fill-color: transparent !important;
  }

  #dvl-agency.dvl-agency--ready .dvl-agency__tagline {
    opacity: 1 !important;
    visibility: visible !important;
  }

  #dvl-agency.dvl-agency--ready .dvl-agency__w {
    opacity: 0;
    visibility: hidden;
  }

  /* Section 2 Card in landscape mobile */
  #dvl-agency .dvl-agency__card,
  #dvl-agency.dvl-agency--flow .dvl-agency__card {
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
    align-items: center !important;
    text-align: center !important;
    gap: clamp(6px, 2vh, 12px) !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    padding-left: max(var(--dvl-agency-pad), env(safe-area-inset-left)) !important;
    padding-right: max(var(--dvl-agency-pad), env(safe-area-inset-right)) !important;
    z-index: 6 !important;
  }

  #dvl-agency .dvl-agency__lede,
  #dvl-agency .dvl-agency__sub {
    width: 100% !important;
    max-width: 92% !important;
    text-align: center !important;
    margin-left: auto !important;
    margin-right: auto !important;
    font-size: clamp(16px, 4.2vh, 22px) !important;
    line-height: 1.25 !important;
  }

  #dvl-agency .dvl-agency__ln,
  #dvl-agency .dvl-agency__lni {
    text-align: center !important;
  }

  #dvl-agency.dvl-agency--ready .dvl-agency__ln {
    clip-path: none !important;
    opacity: 1 !important;
  }

  #dvl-agency.dvl-agency--ready .dvl-agency__lni {
    will-change: transform, opacity !important;
  }

  #dvl-agency .dvl-agency__outro {
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
    align-items: center !important;
    text-align: center !important;
    padding-top: calc(var(--dvl-agency-header-clear, 48px) + 1vh) !important;
    padding-bottom: calc(var(--dvl-agency-footer-h, 60px) + 1vh) !important;
    padding-left: max(var(--dvl-agency-pad), env(safe-area-inset-left)) !important;
    padding-right: max(var(--dvl-agency-pad), env(safe-area-inset-right)) !important;
  }

  #dvl-agency .dvl-agency__outro-head {
    text-align: center !important;
    margin: 0 auto !important;
    max-width: 18em !important;
    font-size: clamp(24px, 7vh, 40px) !important;
  }
}


/* ==========================================================================
   REDUCED MOTION
   --------------------------------------------------------------------------
   The module checks the same query and skips the sequence entirely, so this
   block only has to guarantee that nothing is left hidden by a start state
   that now has no timeline to undo it.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  #dvl-agency.dvl-agency--ready .dvl-agency__ln {
    clip-path: none;
    opacity: 1;
  }
  /* The mobile reveal's start state lives on the wrapper, so releasing the
     line alone would leave the copy sitting 100% below its own mask - i.e.
     invisible, on the one setting where nothing is going to move it. */
  #dvl-agency.dvl-agency--ready .dvl-agency__lni {
    opacity: 1;
    transform: none;
  }
  /* Same reasoning for the flow card: its start state is an opacity in the
     stylesheet, and with the module skipped nothing would ever raise it. */
  #dvl-agency.dvl-agency--ready.dvl-agency--flow .dvl-agency__card {
    opacity: 1;
  }
  #dvl-agency.dvl-agency--ready .dvl-agency__item {
    opacity: 1;
    transform: none;
  }
  #dvl-agency.dvl-agency--ready .dvl-agency__tagline {
    opacity: 1;
    visibility: visible;
  }
  #dvl-agency .dvl-agency__card {
    transform: none;
    box-shadow: none;
  }
}
