/*
 * ============================================================================
 * DREAMSPIRE ELEMENTOR DESIGN SYSTEM (DSG)
 * ============================================================================
 *
 * OWNERSHIP
 *   Elementor owns:  global colors, global typography, H1-H6, body text,
 *                    content width, and per-element layout (flex direction,
 *                    gap, alignment, order).
 *   This file owns:  reusable component classes and every value they use.
 *
 * THE THREE RULES
 *   1. Never target a generated id (.elementor-element-xxxxxxx). Only classes.
 *   2. Never set a colour, radius, blur or shadow on a single element in
 *      Elementor. Use a class, or a global. If a value is truly one-off,
 *      it still gets a named class (e.g. .dsg-button-special).
 *   3. Classes never override each other. A component READS variables,
 *      a modifier WRITES variables. There is no rule in this file that
 *      re-declares a property another DSG class already declared.
 *
 * WHY NO @layer
 *   Elementor and Essential Addons ship unlayered CSS, and unlayered styles
 *   beat every cascade layer regardless of specificity. Wrapping this file in
 *   @layer would make it lose. The section order below is the contract instead.
 *
 * SECTIONS
 *   1  Tokens            all tunable values, nothing else
 *   2  Base              page, sky, hidden code holder
 *   3  Layout            geometry only: section, split, card grid
 *   4  Box               size and spacing only: panel, card, tile, header
 *   5  Surface           paint only: glass, ring, shadow, blur
 *   6  Type and buttons  small roles that Elementor globals do not cover
 *   7  Duo               two columns with an accent line, grid areas only
 *   8  Bridge            unavoidable reach into Essential Addons markup
 *   9  SVG hooks         data-attribute hooks driven by the plugin JS
 *
 * FULL RULEBOOK
 *   readme.txt in this plugin folder. Read it before your first edit: it has
 *   the panel types, the spacing contract and the two traps that have bitten
 *   every previous pass. This header is the short version.
 *
 * HOW TO TUNE
 *   Change a value in section 1. Nothing else. Want every panel less blurry?
 *   --dsg-glass-blur. Want warmer borders everywhere? --dsg-border-default.
 *   Want one panel different? Add a modifier class that writes a variable.
 * ============================================================================
 */


/* ============================================================================
 * 1  TOKENS
 * ========================================================================= */

:root {
  /* --- Motion ------------------------------------------------------------
         A visitor can ask their system for less movement, and this decides
         whether we listen. It ships at 1: we do not. Set it to 0 and the two
         things that move on their own account -- a number counting up and the
         card strip gliding to the next card -- land on their end state at once
         for anyone with the preference switched on.

         It reaches those two through --dsg-motion, which every duration in
         this file is multiplied by, so nothing is written down twice and the
         scripts never have to ask about the preference at all.
      --------------------------------------------------------------------- */
  --ignore-reduced-motien-preference: 1;
  --dsg-motion: 1;

  /* --- Page rhythm ------------------------------------------------------ */
  --dsg-content-width: 1280px;
  --dsg-scroll-offset: 96px;
  --dsg-section-space: clamp(72px, 8vw, 112px);
  --dsg-section-inline-space: 0px;
  --dsg-section-align: center;

  /* THE ONE LEAD-IN. Every section has exactly this much room above its
     panel, so the gap before any panel is the same gap you see before the
     first one. Nothing else contributes: sections carry no bottom padding
     and no minimum height, so a section is always lead-in + panel. */
  --dsg-section-space-before: clamp(400px, 38svh, 550px);
  /* A panorama section is mostly there to let the sky through, so the room
     above it is its own token. On a wide screen it is worth the same as
     every other section's; the phone query below is where they part. */
  --dsg-section-space-panorama: var(--dsg-section-space-before);
  --dsg-section-space-after: 0px;

  /* ---------------------------------------------------------------------
     THE THREE PANEL TYPES

     A panel's height is decided by its type, never by what is inside it.
     Two panels of the same type are therefore exactly the same size on a
     given screen, and a template dropped in anywhere fits immediately.

     These are min-heights, so a panel can still grow if someone puts more
     in it than the type allows. If that happens the type has drifted:
     either shorten the content or raise the number here, but do not let
     one panel quietly become taller than its siblings.

     "cards" is the block that holds the section heading AND the card row,
     not the row on its own, so a card block matches a panel of another type
     the same way.

     Current content demand across 1025-2048px wide, for reference:
       panorama  586    content  1225    cards  826
     --------------------------------------------------------------------- */
  --dsg-panel-panorama-height: 600px;
  --dsg-panel-content-height: 1240px;
  --dsg-panel-content-short-height: 780px;
  --dsg-panel-cards-height: 840px;

  /* --- Panel ------------------------------------------------------------ */
  --dsg-panel-padding: clamp(34px, 5vw, 72px);
  --dsg-panel-radius: 15px;
  --dsg-panel-width: min(100%, var(--dsg-content-width));

  /* --- Card ------------------------------------------------------------- */
  --dsg-card-min-height: 360px;
  --dsg-card-padding: clamp(24px, 3vw, 34px);
  --dsg-card-radius: 22px;
  --dsg-card-gap: 24px;
  --dsg-card-min-width: 260px;
  --dsg-card-grid-columns: repeat(auto-fit, minmax(min(var(--dsg-card-min-width), 100%), 1fr));

  /* --- Card strip: one row of cards that wraps on wide screens and turns
         into a swipeable strip below the stacking breakpoint. Everything
         that differs between the two modes is one of these values, so the
         rules in section 3 never change. --------------------------------- */
  /* --- Card strip -------------------------------------------------------
         One row of cards that lays itself out as a grid while there is room
         for them and as a scrollable line once there is not. The script
         measures the room, puts .dsg-strip--line on the wrapper when the
         grid no longer fits and writes how many cards go side by side; the
         values below are what those two decisions then mean. A card keeps
         its proportions in both, so it only ever scales as a whole.
      --------------------------------------------------------------------- */
  --dsg-card-strip-card-ratio: 0.72;
  --dsg-card-strip-card-min: 260px;
  --dsg-card-strip-card-target: 300px;
  /* The padding is a share of the card width, kept as a bare number because
     the width it multiplies is the flex basis, which only exists further
     down. A plain percentage would not do: padding on a flex item resolves
     against the row, not against the item. The gap and the type are
     container units instead, because inside a card the card is the
     container. */
  --dsg-card-strip-card-padding: 0.115;
  --dsg-card-strip-card-gap: 7.4cqw;
  --dsg-card-strip-stack-gap: var(--dsg-card-strip-card-gap);
  --dsg-card-strip-card-text-size: 5.8cqw;
  --dsg-card-strip-card-line-height: 1.42;
  --dsg-card-strip-card-eyebrow-size: 5cqw;
  --dsg-card-strip-card-title-size: 9cqw;
  --dsg-card-strip-card-title-line-height: 1.2;
  --dsg-card-strip-card-figure-size: 20cqw;

  /* --- Figure: a number that counts up when it comes into view, on its own
         or inside a ring that fills to the same share. Sized in container
         units like everything else in a card, so it scales with the card.
      --------------------------------------------------------------------- */
  --dsg-figure-gap: 5cqw;
  --dsg-figure-direction: column;
  --dsg-figure-align: center;
  --dsg-figure-text-align: center;
  --dsg-figure-width: auto;
  /* The ring block is held back from the full card width so its caption
     breaks into two even lines instead of one long one and an orphan. */
  --dsg-figure-dial-block-width: 91%;
  --dsg-figure-value-size: 13cqw;
  --dsg-figure-value-weight: 700;
  --dsg-figure-caption-size: 5.2cqw;
  --dsg-figure-caption-color: var(--dsg-muted);
  --dsg-figure-dial-size: 43cqw;
  --dsg-figure-dial-thickness: 13;
  --dsg-figure-dial-value-size: calc(var(--dsg-figure-dial-value-share) * var(--dsg-figure-dial-size));
  --dsg-figure-dial-value-share: 0.233;
  --dsg-figure-dial-track: rgba(255, 255, 255, 0.13);
  --dsg-figure-dial-color: var(--dsg-amber);
  --dsg-figure-duration: calc(1.6s * var(--dsg-motion));

  /* Two figures standing next to each other, and staying next to each other
     on a phone. The ring is sized as a share of the half it sits in, which a
     capped size gives, until the viewport comes within reach of it. It is
     written that way rather than as a share of the half, because a share of
     something still being measured leaves the block above unable to work out
     how tall this is; a card gets away with cqw only because a card is a
     container in its own right. The type sizes are swapped by hand. */
  --dsg-figure-pair-width: 620px;
  --dsg-figure-pair-gap: 4%;
  --dsg-figure-pair-figure-gap: 16px;
  --dsg-figure-pair-dial-size: min(185px, 27vw);
  --dsg-figure-pair-value-size:
    calc(var(--dsg-figure-dial-value-share) * var(--dsg-figure-pair-dial-size));
  --dsg-figure-pair-caption-size: 19px;

  /* --- Bar chart ---------------------------------------------------------
     The width is held rather than shared: the block keeps this size while the
     container around it shrinks, and gives way only once the container is the
     narrower of the two. Below the tablet breakpoint that token becomes 100%.
     The icon size is a row the bar cannot use, so the room left for the bar is
     a number this file can work out; the bar width it is capped against keeps
     an icon from overhanging its own bar on a phone. -------------------- */
  --dsg-bars-width: 620px;
  --dsg-bars-height: 260px;
  --dsg-bars-gap: 26px;
  --dsg-bars-bar-width: 74%;
  --dsg-bars-bar-radius: 6px;
  --dsg-bars-rule-width: 1px;
  --dsg-bars-rule-color: var(--dsg-figure-dial-track);
  --dsg-bars-icon-size: 58px;
  --dsg-bars-icon-gap: 14px;
  --dsg-bars-value-gap: 12px;
  --dsg-bars-value-size: 30px;
  --dsg-bars-caption-gap: 10px;
  --dsg-bars-caption-size: 21px;
  --dsg-bars-ramp-1: #8b5cf6;
  --dsg-bars-ramp-2: var(--dsg-aqua);
  --dsg-bars-ramp-3: var(--dsg-teal);
  --dsg-bars-ramp-4: var(--dsg-orange);

  /* --- Research fields ---------------------------------------------------
     The three fields growing until their common ground appears. The picture
     is an svg of its own with a progress function of its own; these are the
     only values it reads from here. The four colours are the ramp the bar
     chart walks, with Game Design in the orange that chart keeps for the
     point being made, so the two diagrams read as one family.

     Snappiness is the svg's own easing: 1 is linear, higher makes the ends
     slower and the middle quicker. The duration is fed to it linearly, so
     this is the only shaping there is.

     At a high snappiness the two ends of that curve are so flat that the
     first and last stretch of a run look like nothing is happening. Trim is
     how much the drawing has to change before a step counts as movement:
     the script walks the whole range once, finds where the picture first
     and last changes by that much, and plays only the part between them, so
     the duration is all time the eye can see. 0 hands over the whole range.

     Width is a cap rather than a size. At 100% the diagram is as wide as
     whatever holds it, and as tall as it is wide.
     --------------------------------------------------------------------- */
  --dsg-fields-width: 100%;
  --dsg-fields-duration: calc(5s * var(--dsg-motion));
  --dsg-fields-snappiness: 7;
  --dsg-fields-trim: 0.002;
  /* The drawing's own viewBox, so the box it sits in is exactly as tall as
     the picture. Crop the drawing and this is the one number to follow. */
  --dsg-fields-ratio: 122.1 / 112.8;
  --dsg-fields-gt: var(--dsg-bars-ramp-1);
  --dsg-fields-sd: var(--dsg-bars-ramp-2);
  --dsg-fields-mp: var(--dsg-bars-ramp-3);
  --dsg-fields-gd: var(--dsg-bars-ramp-4);

  --dsg-card-strip-image-ratio: 1.6;
  /* What .dsg-card-strip--tall swaps in: a card that carries a heading and
     a figure as well as a picture and a paragraph needs more room, and a
     title banner is wider than a portrait. Both are still one card shape,
     scaled as a whole, with the same relative padding. */
  --dsg-card-strip-card-ratio-tall: 0.60;
  --dsg-card-strip-image-ratio-banner: 2.14;
  --dsg-card-strip-peek: 42px;
  --dsg-card-strip-shadow-room: 72px;
  /* How long one press of an arrow takes to reach the next card. The
     script reads it from here rather than holding a number of its own. */
  --dsg-card-strip-step-duration: calc(0.2s * var(--dsg-motion));
  --dsg-card-strip-columns: 1;
  --dsg-panel-cards-strip-height: 0px;

  /* The grid is what a strip does unless the wrapper says otherwise, so
     these are the grid values and .dsg-strip--line is the only override. */
  --dsg-card-strip-wrap: wrap;
  --dsg-card-strip-justify: center;
  --dsg-card-strip-overflow: visible;
  --dsg-card-strip-snap-type: none;
  --dsg-card-strip-snap-align: none;
  --dsg-card-strip-edge: 0px;
  --dsg-card-strip-block-space: 0px;
  --dsg-card-strip-width: min(100%, var(--dsg-content-width));
  --dsg-card-strip-bleed: 0px;
  --dsg-card-strip-shadow: var(--dsg-box-shadow-panel);

  /* --- Strip arrows: the two buttons the script hangs on a strip that
         scrolls. They are hidden whenever there is nothing to scroll to,
         so in the grid they never appear. ------------------------------- */
  --dsg-strip-nav-size: 46px;
  --dsg-strip-nav-inset: 10px;
  --dsg-strip-nav-background: var(--dsg-button-primary-background);
  --dsg-strip-nav-border: var(--dsg-button-primary-border);
  --dsg-strip-nav-color: var(--dsg-text);
  --dsg-strip-nav-icon-size: 13px;
  --dsg-strip-nav-icon-width: 2px;
  /* A chevron drawn from two borders of a rotated square sits entirely to
     one side of the box it is drawn in, so it has to be pulled back by half
     its own diagonal to look centred. */
  --dsg-strip-nav-icon-nudge: 35.4%;

  /* --- Tile: the small labelled squares -------------------------------- */
  --dsg-tile-background: rgba(255, 255, 255, 0.045);
  --dsg-tile-border-width: 1px;
  --dsg-tile-border-color: rgba(218, 218, 232, 0.16);
  --dsg-tile-radius: 18px;
  --dsg-tile-padding: 24px 20px;
  --dsg-tile-min-height: 150px;

  /* --- Split ------------------------------------------------------------ */
  --dsg-split-columns: minmax(0, 1.1fr) minmax(280px, 0.9fr);
  --dsg-split-gap: clamp(36px, 5vw, 72px);

  /* --- Panorama --------------------------------------------------------- */
  --dsg-panorama-panel-width: min(100%, var(--dsg-content-width));
  --dsg-panorama-panel-padding: clamp(28px, 2.8vw, 40px);

  /* --- Duo: two columns with an accent line between them ---------------
     Used by the hero and by any section that needs copy on the left, media
     on the right and a line between. Five named slots: copy, media (under
     copy), divider, aside (right), card (under aside). The responsive order
     is a swap of --dsg-duo-areas, nothing else. */
  --dsg-duo-columns:
    minmax(0, 1.1fr)
    var(--dsg-divider-width)
    minmax(280px, 0.9fr);
  --dsg-duo-areas:
    "copy  divider aside"
    "media divider card";
  /* Variant for blocks whose right side is one tall element. */
  --dsg-duo-areas-aside-tall:
    "copy  divider aside"
    "media divider aside";
  /* A duo whose left column is copy alone: one row, so the empty second
     row cannot contribute its gap and push the panel past its type. */
  --dsg-duo-areas-one-row: "copy  divider aside";
  /* Equal columns put the divider on the centre line of the panel. The
     default is deliberately off-centre, which suits a wide copy column. */
  --dsg-duo-columns-even:
    minmax(0, 1fr)
    var(--dsg-divider-width)
    minmax(0, 1fr);
  /* auto auto = both rows hug their content and the pair sits centred.
     auto 1fr = the lower row absorbs whatever height is left, which is what
     you want when the panel type is taller than the content. */
  --dsg-duo-rows: auto auto;
  --dsg-duo-rows-fill: auto 1fr;
  --dsg-duo-row-gap: 22px;
  --dsg-duo-column-gap: calc((var(--dsg-split-gap) - var(--dsg-divider-width)) / 2);

  /* --- Media: rounded crop for images, video and embeds ---------------- */
  --dsg-media-radius: 14px;
  /* The picture a browser is handed when it cannot read the alpha channel of
     a video. WebKit is that browser, and on iOS it is the only engine there
     is, so a WebM with transparency comes out as a black rectangle. An
     animated WebP carries an alpha channel of its own, needs no decoder and
     no permission to play, and loops by itself. */
  --dsg-media-alpha-fallback: url("/wp-content/uploads/2026/08/dsg_content_ani_card_parallax.gif");
  /* Desktop: buttons size to their label. Tablet and below: they share the
     row 3:2. Elementor owns the row itself (direction, gap, padding). */
  --dsg-hero-button-primary-flex: 0 1 auto;
  --dsg-hero-button-secondary-flex: 0 1 auto;
  --dsg-hero-buttons-gap-small: 8px;

  /* --- Divider: the vertical accent line, wherever it is used ---------- */
  --dsg-divider-display: block;
  --dsg-divider-width: 4px;
  --dsg-divider-height: 78%;
  --dsg-divider-radius: 999px;
  --dsg-divider-color-top: #ffd23f;
  --dsg-divider-color-middle: #ffb300;
  --dsg-divider-color-bottom: #f49300;
  --dsg-divider-glow-size: 12px;
  --dsg-divider-glow-color: rgba(255, 183, 0, 0.42);

  /* --- Floating header -------------------------------------------------- */
  --dsg-header-top: 16px;
  --dsg-header-width: min(1180px, calc(100vw - 32px));
  --dsg-header-min-height: 66px;
  --dsg-header-padding: 9px 14px 9px 18px;
  --dsg-header-radius: 17px;
  --dsg-header-brand-max-width: 230px;
  --dsg-header-blur: 14px;
  --dsg-header-saturation: 112%;
  --dsg-header-filter:
    blur(var(--dsg-header-blur)) saturate(var(--dsg-header-saturation));

  /* --- Navigation ------------------------------------------------------- */
  --dsg-nav-button-min-height: 38px;
  --dsg-nav-button-padding: 9px 12px;
  --dsg-nav-button-radius: 10px;
  --dsg-responsive-menu-width: min(340px, calc(100vw - 20px));
  --dsg-responsive-menu-offset: 10px;
  --dsg-responsive-menu-padding: 12px;
  --dsg-responsive-menu-gap: 8px;
  --dsg-responsive-menu-toggle-size: 44px;
  --dsg-responsive-menu-icon-size: 20px;

  /* --- Type roles that H1-H6 do not cover ------------------------------- */
  /* --- Headline shadow: for a heading that stands on the sky with nothing
         behind it, so it does not dissolve into a bright cloud. Applied
         with .dsg-headline-shadow, which passes it to the heading and to
         the eyebrow above it. ------------------------------------------ */
  --dsg-headline-shadow-color: rgba(7, 8, 13, 0.72);
  --dsg-headline-shadow-blur: 16px;
  --dsg-headline-shadow-offset-x: 0px;
  --dsg-headline-shadow-offset-y: 2px;
  --dsg-headline-shadow:
    var(--dsg-headline-shadow-offset-x)
    var(--dsg-headline-shadow-offset-y)
    var(--dsg-headline-shadow-blur)
    var(--dsg-headline-shadow-color);

  --dsg-eyebrow-color: #c39bff;
  --dsg-eyebrow-size: clamp(0.69rem, 0.9vw, 0.8rem);
  --dsg-eyebrow-weight: 700;
  --dsg-eyebrow-line-height: 1.35;
  --dsg-eyebrow-letter-spacing: 0.11em;
  --dsg-symbol-size: clamp(3.2rem, 5vw, 5.1rem);
  --dsg-symbol-weight: 500;
  --dsg-symbol-line-height: 0.94;

  /* --- Button variants -------------------------------------------------- */
  --dsg-button-primary-background: linear-gradient(105deg, #6630d4, #b326a8 55%, #ef7727);
  --dsg-button-primary-border: rgba(255, 168, 85, 0.46);
  --dsg-button-secondary-background: rgba(255, 255, 255, 0.04);
  --dsg-button-secondary-border: rgba(255, 255, 255, 0.14);

  /* A button may carry a mark before its label. The mark is a mask filled
     with the button's own text colour, so it belongs to the button rather
     than being a picture of its own, and one of the .dsg-button-<name>
     classes below chooses which drawing it is cut from. */
  --dsg-button-icon-size: 1.7em;
  --dsg-button-icon-gap: 0.55em;
  /* Whether a mark stands beside its label or above it. Beside, until the
     row breaks and there is no longer width for both on one line. */
  --dsg-button-content-flow: row;
  /* A button is as wide as it needs to be, until the row shares its width
     out and each one has to keep to what it is given. */
  --dsg-button-width: auto;

  /* Where a contact button stands when its row breaks into two, and how it
     shares the width there. At 0 every button's place multiplies out to 0
     and the row reads in the order it is written; the phone query turns
     that on and gives each button room for three to a row. */
  --dsg-button-order-mix: 0;
  --dsg-button-flex: 0 1 auto;

  /* The line of small print under them. Its links are part of the sentence,
     so only their colour tells them apart. */
  --dsg-legal-link: var(--dsg-muted);
  --dsg-legal-link-hover: var(--dsg-text);
  --dsg-button-nav-color: rgba(245, 243, 239, 0.86);
  --dsg-button-nav-size: 0.82rem;
  --dsg-button-nav-weight: 600;
  --dsg-button-nav-hover-color: #fff;
  --dsg-button-nav-hover-border: rgba(195, 155, 255, 0.24);
  --dsg-button-nav-hover-background: rgba(195, 155, 255, 0.10);

  /* --- Glass and depth: the surface look, tune it once for everything --- */
  --dsg-ring-width: 3px;
  --dsg-glass-blur: 5px;
  --dsg-glass-saturation: 106%;
  /* The whole filter, not just its blur, so a screen that cannot afford it
     can be handed none. A backdrop filter of blur(0px) still costs what a
     backdrop filter costs; only none is free. */
  --dsg-glass-filter:
    blur(var(--dsg-glass-blur)) saturate(var(--dsg-glass-saturation));
  --dsg-box-shadow-header: 0 18px 52px rgba(0, 0, 0, 0.31), 0 0 22px rgba(154, 76, 255, 0.08);
  --dsg-box-shadow-panel: 0 34px 68px rgba(0, 0, 0, 0.38), 0 0 30px rgba(191, 72, 201, 0.20);

  /* --- Sky ---------------------------------------------------------------
     Three planes of pictures behind the page, each a row of tiles carrying
     three pictures that cross-fade as the page is scrolled. What tells the
     planes apart is how far they climb and how fast they drift sideways: the
     far one holds still and climbs least, the near one drifts fastest and
     climbs most, and the difference between those numbers is the depth.

     A plane is one height for all three, and each one starts and ends at its
     own offset. The height has to cover the deepest of those offsets: at 120%
     a plane may rise 20% before its lower edge reaches the bottom of the
     screen, which is exactly what the near plane uses.
     --------------------------------------------------------------------- */
  --dsg-sky-fallback: #111526;
  --dsg-sky-plane-height: 120%;

  /* Where each plane stands at the top of the page, and where at the bottom.
     Negative is up. The far plane already starts a little raised, so that the
     ground it shows at the top is not the very bottom of the picture, and
     the middle plane stands a tenth of a screen lower than the other two,
     which is what sets its clouds below the far ones rather than across
     them. Moving a plane up or down is these two together; changing the
     distance between them is how far it travels. */
  --dsg-sky-far-from: -5lvh;
  --dsg-sky-far-to: -10lvh;
  --dsg-sky-mid-from: 10lvh;
  --dsg-sky-mid-to: 0lvh;
  --dsg-sky-near-from: 0lvh;
  --dsg-sky-near-to: -20lvh;

  /* How long one tile takes to drift past, in seconds. 0 stands still. */
  --dsg-sky-far-drift: 0s;
  --dsg-sky-mid-drift: 340s;
  --dsg-sky-near-drift: 190s;
  /* -1 drifts left, 1 drifts right. */
  --dsg-sky-drift-direction: -1;

  /* --- The cloud colours -----------------------------------------------
     Three per cloud plane, in the order the scroll reads them, each written
     as a whole background value, so the gradient itself sits here to be
     edited rather than inside a file. While a plane's ink is 0 this is what
     is actually seen: its tiles are painted out and do nothing but give the
     plane its size. Put real pictures in, set that plane's ink to 1, and the
     colour comes from the files again.

     Each is the shape the placeholder files draw: a white sheen across the
     middle of a tile, over a top-to-bottom wash. 1 is the pale sand of the
     top of the page, 2 the warmer flush in the middle of it, 3 the violet at
     the bottom. The near plane takes the same three a shade deeper. The far
     plane carries photographs and has no colour here. */
  /* Whether a plane's tiles are the picture, or only the measuring stick
     the tile width is taken from. While the clouds are drawn from the
     colours below, their tiles are never painted: they would be by far the
     largest thing on the page and not one pixel of them would be seen. */
  /* Whether a plane is there at all. A phone is handed the sky itself and
     nothing in front of it. */
  --dsg-sky-far-plane: block;
  --dsg-sky-mid-plane: block;
  --dsg-sky-near-plane: block;

  --dsg-sky-far-tiles: block;
  --dsg-sky-mid-tiles: none;
  --dsg-sky-near-tiles: none;

  --dsg-sky-cloud-sheen:
    linear-gradient(
      90deg,
      rgba(255, 255, 255, 0) 0%,
      rgba(255, 255, 255, 0.26) 50%,
      rgba(255, 255, 255, 0) 100%
    );

  /* One pair per colour, the top of a tile and its foot. */
  --dsg-sky-cloud-1-top: #d5bb96;
  --dsg-sky-cloud-1-foot: #85776e;
  --dsg-sky-cloud-2-top: #d29c7b;
  --dsg-sky-cloud-2-foot: #836265;
  --dsg-sky-cloud-3-top: #746488;
  --dsg-sky-cloud-3-foot: #3c3a63;

  /* How much black the near plane takes over the mid one. It carries the
     same three colours a little deeper, being the nearer of the two. */
  --dsg-sky-near-shade: 12%;

  /* The two colours above are taken as written and then lifted: each is
     carried this far towards full light and given this much more colour,
     which is one place to correct the whole sky rather than six. Lift is a
     share of the light a colour has left, so nothing can overshoot. */
  --dsg-sky-cloud-lift: 0.25;
  --dsg-sky-cloud-chroma: 1.25;

  --dsg-sky-cloud-1-top-lit:
    oklch(
      from var(--dsg-sky-cloud-1-top)
      calc(l + (1 - l) * var(--dsg-sky-cloud-lift))
      calc(c * var(--dsg-sky-cloud-chroma))
      h
    );
  --dsg-sky-cloud-1-foot-lit:
    oklch(
      from var(--dsg-sky-cloud-1-foot)
      calc(l + (1 - l) * var(--dsg-sky-cloud-lift))
      calc(c * var(--dsg-sky-cloud-chroma))
      h
    );
  --dsg-sky-cloud-2-top-lit:
    oklch(
      from var(--dsg-sky-cloud-2-top)
      calc(l + (1 - l) * var(--dsg-sky-cloud-lift))
      calc(c * var(--dsg-sky-cloud-chroma))
      h
    );
  --dsg-sky-cloud-2-foot-lit:
    oklch(
      from var(--dsg-sky-cloud-2-foot)
      calc(l + (1 - l) * var(--dsg-sky-cloud-lift))
      calc(c * var(--dsg-sky-cloud-chroma))
      h
    );
  --dsg-sky-cloud-3-top-lit:
    oklch(
      from var(--dsg-sky-cloud-3-top)
      calc(l + (1 - l) * var(--dsg-sky-cloud-lift))
      calc(c * var(--dsg-sky-cloud-chroma))
      h
    );
  --dsg-sky-cloud-3-foot-lit:
    oklch(
      from var(--dsg-sky-cloud-3-foot)
      calc(l + (1 - l) * var(--dsg-sky-cloud-lift))
      calc(c * var(--dsg-sky-cloud-chroma))
      h
    );

  --dsg-sky-mid-1:
    var(--dsg-sky-cloud-sheen),
    linear-gradient(
      180deg,
      var(--dsg-sky-cloud-1-top-lit) 0%,
      var(--dsg-sky-cloud-1-foot-lit) 100%
    );
  --dsg-sky-mid-2:
    var(--dsg-sky-cloud-sheen),
    linear-gradient(
      180deg,
      var(--dsg-sky-cloud-2-top-lit) 0%,
      var(--dsg-sky-cloud-2-foot-lit) 100%
    );
  --dsg-sky-mid-3:
    var(--dsg-sky-cloud-sheen),
    linear-gradient(
      180deg,
      var(--dsg-sky-cloud-3-top-lit) 0%,
      var(--dsg-sky-cloud-3-foot-lit) 100%
    );

  --dsg-sky-near-1:
    var(--dsg-sky-cloud-sheen),
    linear-gradient(
      180deg,
      color-mix(in srgb, #000 var(--dsg-sky-near-shade), var(--dsg-sky-cloud-1-top-lit)) 0%,
      color-mix(in srgb, #000 var(--dsg-sky-near-shade), var(--dsg-sky-cloud-1-foot-lit)) 100%
    );
  --dsg-sky-near-2:
    var(--dsg-sky-cloud-sheen),
    linear-gradient(
      180deg,
      color-mix(in srgb, #000 var(--dsg-sky-near-shade), var(--dsg-sky-cloud-2-top-lit)) 0%,
      color-mix(in srgb, #000 var(--dsg-sky-near-shade), var(--dsg-sky-cloud-2-foot-lit)) 100%
    );
  --dsg-sky-near-3:
    var(--dsg-sky-cloud-sheen),
    linear-gradient(
      180deg,
      color-mix(in srgb, #000 var(--dsg-sky-near-shade), var(--dsg-sky-cloud-3-top-lit)) 0%,
      color-mix(in srgb, #000 var(--dsg-sky-near-shade), var(--dsg-sky-cloud-3-foot-lit)) 100%
    );

  /* How tall a plane's pictures stand in it, and where in it they stand. At
     100% a picture is as tall as its plane, which means its width, and so
     how large a cloud comes out, follows the height of the screen. On a
     phone that makes for large clouds, and shrinking the band in the phone
     query is the one place to answer that. The mask is sized by the same
     token, so a picture and its mask cannot come apart. */
  --dsg-sky-plane-band: 100%;
  --dsg-sky-plane-anchor: flex-start;
  --dsg-sky-plane-band-y: 0%;

  /* What a tilted phone is worth: how far a plane may be pushed sideways and
     lifted, how much of that each plane takes, and the angle at which a tilt
     counts as fully over. --dsg-sky-tilt-ask at 0 stops iOS being asked for
     the permission it wants before it will report any of this at all. */
  --dsg-sky-tilt-reach: 6vw;
  --dsg-sky-tilt-lift: 1.5lvh;
  --dsg-sky-far-tilt: 0.25;
  --dsg-sky-mid-tilt: 0.6;
  --dsg-sky-near-tilt: 1;
  --dsg-sky-tilt-range: 38;
  --dsg-sky-tilt-ask: 1;

  /* The soft follow. Nothing about the sky maps straight onto the scroll
     position: a rendered figure chases the real one at this rate and may
     never move faster than this share of the page per second, which is what
     keeps a flicked scroll from snapping the sky across. */
  /* Whether the sky follows the scroll softly or simply is where the scroll
     is. The softness exists to take the steps out of a mouse wheel; a finger
     is smooth already, so a phone is handed 0. At 0 there is no frame loop
     at all: the sky is painted on scroll, and drift and tilt, which need a
     clock rather than a scroll position, do not run. */
  --dsg-sky-follow: 1;
  --dsg-sky-follow-response: 8;
  --dsg-sky-follow-max-speed: 0.42;

  --dsg-sky-shade:
    linear-gradient(180deg, rgba(2, 3, 8, 0.10), transparent 38%, rgba(2, 3, 8, 0.38)),
    radial-gradient(circle at 50% 35%, transparent 32%, rgba(3, 4, 9, 0.22) 100%);

  /* --- Colours ---------------------------------------------------------- */
  --dsg-ink: #07080d;
  --dsg-text: #f5f3ef;
  --dsg-muted: #aaa9b2;
  --dsg-violet: #9a4cff;
  --dsg-magenta: #ef3d9e;
  --dsg-orange: #ff8b22;
  --dsg-amber: #ffd23f;
  --dsg-aqua: #71bcff;
  --dsg-teal: #2ed3c4;

  /* --- Surface fills ---------------------------------------------------- */
  --dsg-glass: linear-gradient(
    112deg,
    rgba(29, 32, 52, 0.75) 0%,
    rgba(43, 24, 67, 0.75) 50%,
    rgba(57, 34, 23, 0.75) 100%
  );
  --dsg-ink-glass: linear-gradient(
    112deg,
    rgba(16, 18, 31, 0.76),
    rgba(29, 18, 46, 0.74) 54%,
    rgba(42, 25, 20, 0.72)
  );

  /* --- Border gradients: pick one with a .dsg-outline-* class ----------- */
  --dsg-border-default:  linear-gradient(115deg, rgba(195, 155, 255, 0.92), rgba(239, 61, 158, 0.78) 52%, rgba(255, 139, 34, 0.88));
  --dsg-border-ink:      linear-gradient(115deg, rgba(91, 98, 137, 0.76), rgba(76, 55, 101, 0.72) 54%, rgba(105, 70, 53, 0.70));
  --dsg-border-violet:   linear-gradient(120deg, rgba(206, 177, 255, 0.98), rgba(164, 76, 255, 0.82) 48%, rgba(239, 61, 158, 0.68));
  --dsg-border-magenta:  linear-gradient(120deg, rgba(199, 147, 255, 0.82), rgba(255, 76, 178, 0.96) 50%, rgba(255, 139, 34, 0.68));
  --dsg-border-orange:   linear-gradient(120deg, rgba(239, 61, 158, 0.66), rgba(255, 139, 34, 0.98) 55%, rgba(255, 206, 126, 0.82));
  --dsg-border-panorama: linear-gradient(120deg, rgba(206, 177, 255, 0.96), rgba(239, 61, 158, 0.82) 52%, rgba(255, 151, 59, 0.90));
  --dsg-border-card-1:   linear-gradient(135deg, rgba(205, 176, 255, 0.95), rgba(154, 76, 255, 0.62), rgba(239, 61, 158, 0.54));
  --dsg-border-card-2:   linear-gradient(135deg, rgba(197, 145, 255, 0.72), rgba(255, 76, 178, 0.92), rgba(255, 139, 34, 0.58));
  --dsg-border-card-3:   linear-gradient(135deg, rgba(239, 61, 158, 0.60), rgba(255, 139, 34, 0.94), rgba(255, 208, 133, 0.72));
  --dsg-border-card-4:   linear-gradient(135deg, rgba(113, 188, 255, 0.92), rgba(154, 76, 255, 0.74), rgba(239, 61, 158, 0.56));
}

/* Tablet and below */
@media (max-width: 1024px) {
  :root {
    /* Stacked layouts cannot honour a fixed panel height, so below this
       breakpoint every type falls back to its content. */
    --dsg-panel-panorama-height: 0px;
    --dsg-panel-content-height: 0px;
    --dsg-panel-content-short-height: 0px;
    --dsg-panel-cards-height: 0px;
    --dsg-split-columns: 1fr;
    --dsg-duo-columns: minmax(0, 1fr);
    --dsg-duo-columns-even: minmax(0, 1fr);
    --dsg-panel-cards-strip-height: 0px;
    --dsg-duo-areas: "copy" "aside" "media";
    --dsg-duo-areas-aside-tall: "copy" "aside" "media";
    --dsg-duo-areas-one-row: "copy" "aside";
    --dsg-divider-display: none;
    --dsg-hero-button-primary-flex: 3 1 0;
    --dsg-hero-button-secondary-flex: 2 1 0;
    --dsg-bars-width: 100%;
    --dsg-bars-height: 220px;
    --dsg-bars-gap: 18px;
    --dsg-bars-icon-size: 44px;
    --dsg-bars-icon-gap: 10px;
    --dsg-bars-value-size: 26px;
    --dsg-bars-caption-size: 20px;
    --dsg-figure-pair-width: 100%;
    --dsg-figure-pair-figure-gap: 12px;
    --dsg-figure-pair-caption-size: 16px;
  }
}

/* Phone */
@media (max-width: 767px) {
  :root {
    --dsg-scroll-offset: 82px;
    --dsg-section-space: 58px;
    --dsg-section-inline-space: 12px;
    --dsg-panel-padding: 32px 24px;
    --dsg-panel-radius: 20px;
    --dsg-card-min-height: 0;
    --dsg-card-grid-columns: 1fr;
    --dsg-header-top: 10px;
    --dsg-header-width: calc(100vw - 20px);
    --dsg-header-min-height: 60px;
    --dsg-header-padding: 8px 12px;
    --dsg-header-radius: 15px;
    --dsg-section-space-before: clamp(150px, 28svh, 220px);
    --dsg-section-space-panorama: clamp(225px, 42svh, 330px);

    /* A phone has far less screen for a plane to climb in, so the whole
       climb is cut to 15lvh at the most and the order is turned around:
       the background travels furthest and the near clouds least. Far 15,
       mid 10, near 5 - the middle plane already travels 10, so only the
       outer two are named here. Each keeps the offset it starts from. */
    --dsg-sky-far-to: -20lvh;
    --dsg-sky-near-to: -5lvh;

    --dsg-button-order-mix: 1;
    --dsg-button-flex: 1 1 28%;
    --dsg-button-content-flow: column;
    --dsg-button-icon-gap: 0.3em;
    --dsg-button-width: 100%;
    --dsg-panorama-panel-width: 100%;
    --dsg-panorama-panel-padding: 30px 24px;
    --dsg-bars-height: 186px;
    --dsg-bars-gap: 12px;
    --dsg-bars-icon-size: 34px;
    --dsg-bars-icon-gap: 8px;
    --dsg-bars-value-gap: 8px;
    --dsg-bars-value-size: 23px;
    --dsg-bars-caption-size: 18px;
    --dsg-figure-pair-figure-gap: 10px;
    --dsg-figure-pair-caption-size: 12px;

    /* A backdrop filter reads the pixels behind an element and blurs them
       again on every frame the element or its backdrop moves. Behind these
       panels sits a fixed sky that moves the whole time, so on a phone that
       is a full-screen blur per panel per frame, and it is what a phone
       answers by dropping frames and by not drawing what it cannot keep up
       with. The glass keeps its fill and its edge; only the blur goes. */
    --dsg-glass-filter: none;
    --dsg-header-filter: none;

    /* A phone gets the sky and nothing in front of it, and nothing that
       moves by itself: no clouds, no climb, no drift, no tilt, and no soft
       follow. What is left is three pictures cross-fading with the scroll,
       painted when the page is scrolled and at no other time. */
    --dsg-sky-mid-plane: none;
    --dsg-sky-near-plane: none;

    --dsg-sky-far-to: -5lvh;
    --dsg-sky-mid-to: 10lvh;
    --dsg-sky-near-to: 0lvh;

    --dsg-sky-mid-drift: 0s;
    --dsg-sky-near-drift: 0s;

    --dsg-sky-tilt-reach: 0px;
    --dsg-sky-tilt-lift: 0px;
    --dsg-sky-tilt-ask: 0;

    --dsg-sky-follow: 0;
  }

  /* The one rule the phone query carries, and the reason it is a rule and
     not a token: three contact buttons to a row leave each about a third of
     a phone wide, which is less than the label needs at the side padding a
     full-width button is given. Only the sides are taken in, and only for
     this row; nothing else in the file declares this property, so nothing
     is being overridden. */
  .dsg-contact-buttons .elementor-button {
    padding-inline: 8px;
  }
}


/* ============================================================================
 * 2  BASE
 * ========================================================================= */

html:has(.dsg-page) {
  /* The card strip runs the full width of the screen, and on a window narrow
     enough to have a classic scrollbar 100vw is that scrollbar wider than the
     page. Clipping here as well as on the body keeps the page from being
     draggable sideways by those few pixels. */
  overflow-x: clip;
  scroll-behavior: smooth;
  scroll-padding-top: var(--dsg-scroll-offset);
  background: var(--dsg-ink);
}

body:has(.dsg-page) {
  overflow-x: clip;
  background: var(--dsg-ink);
}

@media (prefers-reduced-motion: reduce) {
  html:has(.dsg-page) {
    scroll-behavior: auto;
  }

  /* The preference reaches the durations through this one multiplier, and
     whether it reaches them at all is --ignore-reduced-motien-preference at
     the top of the file. While that stays at 1 this line changes nothing. */
  :root {
    --dsg-motion: var(--ignore-reduced-motien-preference);
  }
}

.dsg-page {
  position: relative;
  isolation: isolate;
  padding-bottom: var(--dsg-section-space-before);
  background: transparent;
}

/* Holder for the HTML widget that carries the sky markup. Takes no space. */
.dsg-code {
  position: static;
  flex: 0 0 0;
  width: 0;
  min-width: 0;
  height: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
  pointer-events: none;
}

.dsg-sky {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background: var(--dsg-sky-fallback);
}

/* One plane. It is inset sideways by half a tile plus the reach of a tilt, so
   it can drift and be pushed without ever bringing an edge into view, and it
   is taller than the screen by exactly the distance it climbs. The inset
   stays at zero until the script has measured a tile, so a plane that never
   drifts never asks for the room. Climb and drift share one transform,
   because an element only has one. */
.dsg-sky-plane {
  display: var(--dsg-sky-plane-display, block);
  position: absolute;
  top: 0;
  right: calc(-1 * var(--dsg-sky-plane-room, 0px));
  left: calc(-1 * var(--dsg-sky-plane-room, 0px));
  z-index: var(--dsg-sky-plane-depth, 1);
  height: var(--dsg-sky-plane-height);
  transform:
    translate3d(
      var(--dsg-sky-plane-shift, 0px),
      calc(
        var(--dsg-sky-plane-from)
        + (var(--dsg-sky-plane-to) - var(--dsg-sky-plane-from))
        * var(--dsg-sky-rise, 0)
        + var(--dsg-sky-plane-lift, 0px)
      ),
      0
    );
  will-change: transform;
  -webkit-mask-image: var(--dsg-sky-plane-mask, none);
  -webkit-mask-repeat: repeat-x;
  -webkit-mask-size: var(--dsg-sky-plane-tile, auto) var(--dsg-sky-plane-band);
  -webkit-mask-position: var(--dsg-sky-plane-slide, 0px) var(--dsg-sky-plane-band-y);
  -webkit-mask-source-type: var(--dsg-sky-plane-mask-mode, alpha);
  mask-image: var(--dsg-sky-plane-mask, none);
  mask-repeat: repeat-x;
  mask-size: var(--dsg-sky-plane-tile, auto) var(--dsg-sky-plane-band);
  mask-position: var(--dsg-sky-plane-slide, 0px) var(--dsg-sky-plane-band-y);
  mask-mode: var(--dsg-sky-plane-mask-mode, alpha);
}

/* One picture of a plane, laid out as the row of tiles that covers it. Only
   its opacity and, while the placeholders are in, its colour are ever
   touched, and the opacity only ever from the one scroll figure every
   plane reads. The first picture written is the one on top: they stack in the
   order they appear, so the last one is the ground the others uncover rather
   than the one that covers them. */
.dsg-sky-layer {
  position: absolute;
  inset: 0;
  z-index: var(--dsg-sky-layer-depth, 1);
  display: flex;
  align-items: var(--dsg-sky-plane-anchor);
  justify-content: var(--dsg-sky-plane-justify, flex-start);
  background-image: var(--dsg-sky-layer-colour, none);
  background-repeat: repeat-x;
  background-size: var(--dsg-sky-plane-tile, auto) var(--dsg-sky-plane-band);
  background-position: var(--dsg-sky-plane-slide, 0px) var(--dsg-sky-plane-band-y);
  opacity: var(--dsg-sky-layer-opacity, 1);
}

/* The stack, counted from the back, so the pictures are in the right order
   before the script has said a word: the last one written is the ground and
   the first one written covers it. The script writes the same numbers; these
   are what the Elementor editor and a page with no JavaScript see. */
.dsg-sky-layer:nth-last-child(2) { --dsg-sky-layer-depth: 2; }
.dsg-sky-layer:nth-last-child(3) { --dsg-sky-layer-depth: 3; }
.dsg-sky-layer:nth-last-child(4) { --dsg-sky-layer-depth: 4; }

/* A tile is the picture at band height. Its width comes from its own
   proportions, but as a whole number of pixels that the script works out and
   the mask above is sized by as well: left to fall where it may, a fractional
   width leaves a hairline of nothing between one tile and the next, and walks
   the mask a little further out of step with every tile it crosses.

   max-width has to be undone by hand. The theme sets it on every image there
   is, which would otherwise squeeze a tile into the screen. */
.dsg-sky .dsg-sky-tile {
  flex: 0 0 auto;
  width: var(--dsg-sky-plane-tile, auto);
  height: var(--dsg-sky-plane-band);
  max-width: none;
  display: var(--dsg-sky-plane-tiles, block);
  transform: scaleX(var(--dsg-sky-tile-flip, 1));
}

/* What each plane is worth. Each writes variables and nothing else. The far
   one is centred rather than started from the left, because it is the one
   whose tiles are mirrored and the original belongs in the middle. */
.dsg-sky-plane--far {
  --dsg-sky-plane-from: var(--dsg-sky-far-from);
  --dsg-sky-plane-to: var(--dsg-sky-far-to);
  --dsg-sky-plane-display: var(--dsg-sky-far-plane);
  --dsg-sky-plane-tiles: var(--dsg-sky-far-tiles);
  --dsg-sky-plane-justify: center;
  --dsg-sky-plane-depth: 1;
}

.dsg-sky-plane--mid {
  --dsg-sky-plane-from: var(--dsg-sky-mid-from);
  --dsg-sky-plane-to: var(--dsg-sky-mid-to);
  --dsg-sky-plane-display: var(--dsg-sky-mid-plane);
  --dsg-sky-plane-tiles: var(--dsg-sky-mid-tiles);
  --dsg-sky-plane-depth: 2;
}

.dsg-sky-plane--near {
  --dsg-sky-plane-from: var(--dsg-sky-near-from);
  --dsg-sky-plane-to: var(--dsg-sky-near-to);
  --dsg-sky-plane-display: var(--dsg-sky-near-plane);
  --dsg-sky-plane-tiles: var(--dsg-sky-near-tiles);
  --dsg-sky-plane-depth: 3;
}

.dsg-sky__shade {
  position: absolute;
  inset: 0;
  z-index: 9;
  background: var(--dsg-sky-shade);
}


/* ============================================================================
 * 3  LAYOUT  -  geometry only, never paint
 * ========================================================================= */

/* Every section is the same shape: the lead-in, then its panel. No minimum
   height and no bottom padding, so nothing can add invisible space and
   nothing centres a panel in leftover room. That is what keeps the gap
   before every panel identical. */
.dsg-section {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  justify-content: var(--dsg-section-align);
  width: 100%;
  padding:
    var(--dsg-section-space-before)
    var(--dsg-section-inline-space)
    var(--dsg-section-space-after);
  scroll-margin-top: var(--dsg-scroll-offset);
  background: transparent;
}

/* A panorama section carries pictures rather than words, and the room above
   it is where the sky is actually seen, so it reads the panorama lead-in
   instead of the shared one. The panel inside already says which sections
   these are, so there is nothing extra to remember on the page. */
.dsg-section:has(> .dsg-panel--panorama) {
  --dsg-section-space-before: var(--dsg-section-space-panorama);
}

.dsg-split {
  display: grid;
  grid-template-columns: var(--dsg-split-columns);
  align-items: center;
  gap: var(--dsg-split-gap);
}

.dsg-card-grid {
  display: grid;
  grid-template-columns: var(--dsg-card-grid-columns);
  align-items: stretch;
  gap: var(--dsg-card-gap);
  width: min(100%, var(--dsg-content-width));
}

/* The same row of cards, but it wraps instead of auto-fitting, and below the
   stacking breakpoint it becomes a horizontally swipeable strip in which the
   neighbouring cards stay half visible at the edges. One row of markup serves
   both, so the cards are written and edited exactly once. The strip sizes its
   own children, so it works with anything put inside it, and adding a card is
   nothing but another child. How many sit side by side, and how far the strip
   is inset so the neighbours peek, are tokens in section 1. */
/* The wrapper the script puts around a strip, so the two arrows have
   something to be positioned against. It also carries the width, so in the
   grid the strip is content width and in the line it runs to the edges of
   the screen. Without the script there is no wrapper and the strip is simply
   as wide as the block it sits in, laid out as a grid. */
.dsg-strip {
  position: relative;
  width: var(--dsg-card-strip-width);
  margin-inline: var(--dsg-card-strip-bleed);
}

/* The one state there is. The script puts it on the wrapper as soon as the
   cards can no longer stand side by side at --dsg-card-strip-card-min, and
   takes it off again when they can. Everything it does is write the values
   the strip already reads; nothing here is a property. */
.dsg-strip--line {
  --dsg-card-strip-wrap: nowrap;
  --dsg-card-strip-justify: flex-start;
  --dsg-card-strip-overflow: auto;
  --dsg-card-strip-snap-type: x mandatory;
  --dsg-card-strip-snap-align: center;
  --dsg-card-strip-edge: var(--dsg-card-strip-peek);
  --dsg-card-strip-block-space: var(--dsg-card-strip-shadow-room);
  --dsg-card-strip-width: 100vw;
  --dsg-card-strip-bleed: calc(50% - 50vw);
  --dsg-card-strip-shadow: var(--dsg-box-shadow-header);
}

.dsg-card-strip {
  /* The card width that follows from the column count the script wrote. It
     is resolved here and not in section 1 because that count lives on this
     element, where a value in :root could not see it. */
  --dsg-card-strip-basis:
    calc((100% - (var(--dsg-card-strip-columns) - 1) * var(--dsg-card-gap))
         / var(--dsg-card-strip-columns));
  --dsg-glass-shadow: var(--dsg-card-strip-shadow);
  --dsg-card-min-height: 0px;
  display: flex;
  flex-wrap: var(--dsg-card-strip-wrap);
  justify-content: var(--dsg-card-strip-justify);
  align-items: flex-start;
  gap: var(--dsg-card-gap);
  width: 100%;
  padding-inline: var(--dsg-card-strip-edge);
  padding-block: var(--dsg-card-strip-block-space);
  overflow-x: var(--dsg-card-strip-overflow);
  overscroll-behavior-x: contain;
  scroll-snap-type: var(--dsg-card-strip-snap-type);
  scrollbar-width: none;
}

/* While a mouse is dragging the strip the snap has to be out of the way, or
   every pixel of the drag fights the snap trying to settle. The state class
   writes the token the strip already reads, it does not redeclare anything. */
/* A card block whose cards hold more than a picture and a paragraph. It
   writes two variables and nothing else, so the cards keep the same padding,
   the same gaps and the same type scale as every other card in a strip. */
.dsg-card-strip--tall {
  --dsg-card-strip-card-ratio: var(--dsg-card-strip-card-ratio-tall);
  --dsg-card-strip-image-ratio: var(--dsg-card-strip-image-ratio-banner);
}

/* While the strip is being dragged or is gliding to the next card the snap
   has to be out of the way, or every frame fights the snap trying to
   settle. Both states write the token the strip already reads. */
.dsg-card-strip.is-dragging,
.dsg-card-strip.is-stepping {
  --dsg-card-strip-snap-type: none;
}

.dsg-card-strip.is-dragging {
  cursor: grabbing;
  user-select: none;
}

.dsg-card-strip::-webkit-scrollbar {
  width: 0;
  height: 0;
}

/* One arrow. The script hangs two of them in the wrapper and marks either
   one hidden as soon as there is nothing left to scroll to on that side, so
   above the breakpoint, where the strip wraps instead of scrolling, both are
   gone without a media query saying so. */
/* The Elementor kit styles every button by element name, which outranks a
   single class, so this one is anchored on the wrapper. */
.dsg-strip > .dsg-strip-nav {
  position: absolute;
  top: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: var(--dsg-strip-nav-size);
  height: var(--dsg-strip-nav-size);
  padding: 0;
  transform: translateY(-50%);
  color: var(--dsg-strip-nav-color);
  border: 1px solid var(--dsg-strip-nav-border);
  border-radius: 999px;
  background: var(--dsg-strip-nav-background);
  box-shadow: var(--dsg-box-shadow-header);
  cursor: pointer;
}

.dsg-strip-nav[hidden] {
  display: none;
}

.dsg-strip-nav--prev {
  inset-inline-start: var(--dsg-strip-nav-inset);
}

.dsg-strip-nav--next {
  inset-inline-end: var(--dsg-strip-nav-inset);
}

/* The chevron, drawn from two borders so it needs no icon font and no SVG. */
.dsg-strip-nav::before {
  content: "";
  width: var(--dsg-strip-nav-icon-size);
  height: var(--dsg-strip-nav-icon-size);
  border-top: var(--dsg-strip-nav-icon-width) solid currentColor;
  border-right: var(--dsg-strip-nav-icon-width) solid currentColor;
}

.dsg-strip-nav--prev::before {
  transform: translateX(var(--dsg-strip-nav-icon-nudge)) rotate(-135deg);
}

.dsg-strip-nav--next::before {
  transform: translateX(calc(-1 * var(--dsg-strip-nav-icon-nudge))) rotate(45deg);
}

/* The container widget ships flex: var(--flex-grow) var(--flex-shrink)
   var(--flex-basis) at exactly this specificity, so the selector carries
   Elementor's own class for the same reason the media rules below do, and
   writes the three variables that rule already reads instead of fighting it.
   Anything else dropped into the strip is sized by the second selector. */
.dsg-card-strip > .e-con {
  --flex-grow: 0;
  --flex-shrink: 0;
  --flex-basis: var(--dsg-card-strip-basis);
  --dsg-card-padding:
    calc(var(--dsg-card-strip-card-padding) * var(--dsg-card-strip-basis));
  --dsg-eyebrow-size: var(--dsg-card-strip-card-eyebrow-size);
  aspect-ratio: var(--dsg-card-strip-card-ratio);
  container-type: inline-size;
  scroll-snap-align: var(--dsg-card-strip-snap-align);
}

.dsg-card-strip > :not(.e-con) {
  flex: 0 0 var(--dsg-card-strip-basis);
  scroll-snap-align: var(--dsg-card-strip-snap-align);
}

/* Inside a card everything is measured against the card, not against the
   page: the padding as a share of its width, the gap, the picture box and
   the type in container units. A card at any size is therefore the same
   picture, so the same amount of text fits into every one of them whatever
   the screen does. This is the one place where the type size is not left to
   the Elementor globals, and it is deliberate. */
.dsg-card-strip .elementor-widget-image img {
  width: 100%;
  aspect-ratio: var(--dsg-card-strip-image-ratio);
  object-fit: cover;
}

/* The space between what is stacked in a card. It is a margin and not the
   container gap because Elementor writes that gap per element at a higher
   specificity, and a value in px there would not scale with the card. */
.dsg-card-strip > .e-con > * + * {
  margin-block-start: var(--dsg-card-strip-stack-gap);
}

/* Put this on the last thing in a card and it is pinned to the bottom of it,
   centred there, however tall or short everything above turns out. The space
   above it becomes whatever is left, which is why it writes the stack gap
   rather than a margin of its own: two rules on one property would be exactly
   the fight this system avoids. The longer selector is only there to outrank
   that stack-gap rule so the anchor holds rather than being handed back the
   ordinary gap; the short one keeps it working in a card built outside the
   strip. */
.dsg-card-strip > .e-con > .dsg-card-bottom,
.dsg-card-bottom {
  --dsg-card-strip-stack-gap: auto;
  align-self: stretch;
  margin-block-start: auto;
  text-align: center;
}

/* The last paragraph keeps its trailing margin otherwise, which quietly
   costs the card a line of the text it was measured to hold. */
.dsg-card-strip .elementor-widget-text-editor p:last-child {
  margin-block-end: 0;
}

.dsg-card-strip .elementor-widget-text-editor,
.dsg-card-strip .elementor-widget-text-editor p {
  font-size: var(--dsg-card-strip-card-text-size);
  line-height: var(--dsg-card-strip-card-line-height);
}

/* --- Figure ---------------------------------------------------------------
   A number that counts up when it scrolls into view, either on its own or
   sitting in a ring that fills to the same share. The markup is the same for
   both; .dsg-figure--dial is what adds the ring, and the value it counts to
   is a data attribute on the block, not a number in here.

     .dsg-figure            the block
     .dsg-figure--dial      with a ring around the number
     .dsg-figure-dial       the ring and the number it holds
     .dsg-figure-value      the number
     .dsg-figure-caption    the line under or beside it

   The ring is one SVG circle whose dash pattern is driven by
   --dsg-figure-dial-progress, which the script animates from 0 to the value.
   Nothing here knows what the number is. --------------------------------- */
/* On its own the number stands above its caption; with a ring it stands
   beside it, because the ring is square and a caption under it would leave
   the card half empty. Both are the same block, the modifier only writes
   which way it runs. */
.dsg-figure {
  display: flex;
  flex-direction: var(--dsg-figure-direction);
  align-items: var(--dsg-figure-align);
  justify-content: center;
  gap: var(--dsg-figure-gap);
  width: var(--dsg-figure-width);
  margin-inline: auto;
  text-align: var(--dsg-figure-text-align);
}

.dsg-figure-value {
  color: var(--dsg-text);
  font-size: var(--dsg-figure-value-size);
  font-weight: var(--dsg-figure-value-weight);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.dsg-figure-caption {
  margin: 0;
  color: var(--dsg-figure-caption-color);
  font-size: var(--dsg-figure-caption-size);
  line-height: 1.3;
}

.dsg-figure-dial {
  position: relative;
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  width: var(--dsg-figure-dial-size);
  aspect-ratio: 1;
}

.dsg-figure-dial svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.dsg-figure-dial circle {
  fill: none;
  stroke-width: var(--dsg-figure-dial-thickness);
}

.dsg-figure-track {
  stroke: var(--dsg-figure-dial-track);
}

/* The circle is drawn at r = 43.5 in a 100 unit box, so its length is
   2 * pi * 43.5 = 273.3. The dash is that length times the progress the
   script publishes, and the gap is the rest. */
.dsg-figure-fill {
  stroke: var(--dsg-figure-dial-color);
  stroke-linecap: round;
  stroke-dasharray:
    calc(273.3 * var(--dsg-figure-dial-progress, 0)) 273.3;
}

.dsg-figure--dial {
  --dsg-figure-direction: row;
  --dsg-figure-align: center;
  --dsg-figure-text-align: start;
  --dsg-figure-width: var(--dsg-figure-dial-block-width);
}

.dsg-figure--dial .dsg-figure-value {
  position: relative;
  font-size: var(--dsg-figure-dial-value-size);
}

/* --- Research fields -------------------------------------------------------
   The svg is inlined rather than linked, so its script runs in this page and
   the properties below reach inside it. It carries its own background rect,
   left unpainted here so the panel shows through.

   The four selectors below name ids, which nothing else in this file does.
   They are ids written by hand in the drawing, not ids Elementor generated,
   and they are the only handle the drawing offers for one field at a time.
   ------------------------------------------------------------------------ */
.dsg-fields {
  --animation-snappiness: var(--dsg-fields-snappiness);
  display: block;
  width: min(100%, var(--dsg-fields-width));
  aspect-ratio: var(--dsg-fields-ratio);
  margin-inline: auto;
}

.dsg-fields svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* The widget holding one is given its width outright. A widget in a column
   is stretched to its container only after its own height has been settled,
   and a drawing that takes its height from its width gets measured at the
   300px an svg falls back to while that width is still unknown. Naming the
   width first settles it, and a widget in a container fills it anyway. */
.elementor-widget:has(.dsg-fields) {
  width: 100%;
}

.dsg-fields #GT { color: var(--dsg-fields-gt); }
.dsg-fields #SD { color: var(--dsg-fields-sd); }
.dsg-fields #MP { color: var(--dsg-fields-mp); }
.dsg-fields #GD { color: var(--dsg-fields-gd); }

.dsg-fields .field {
  fill: currentColor;
  stroke: currentColor;
}

.dsg-fields .field__circle,
.dsg-fields .field__intersection {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.dsg-fields .field__icon,
.dsg-fields .field__text {
  fill: currentColor;
  stroke: none;
}

.dsg-fields .field__text {
  font-family: Roboto, Arial, sans-serif;
  font-weight: 700;
  font-kerning: normal;
  letter-spacing: 0;
  text-anchor: middle;
}

/* --- Tints -----------------------------------------------------------------
   The colour a diagram takes, written once for every diagram there is. A ring
   reads --dsg-figure-dial-color and a bar reads --dsg-bar-color, so one class
   writes both and a tint therefore works for either of them. Nothing else is
   set here: a tint is a colour, not a look.

   The palette names are the palette. The ramp names are the four steps a row
   of bars walks through, and are what a bar falls back to when its markup
   names no colour, so a row comes out in order without anything saying so.
   ------------------------------------------------------------------------ */
.dsg-tint-violet { --dsg-figure-dial-color: var(--dsg-violet); --dsg-bar-color: var(--dsg-violet); }
.dsg-tint-magenta { --dsg-figure-dial-color: var(--dsg-magenta); --dsg-bar-color: var(--dsg-magenta); }
.dsg-tint-orange { --dsg-figure-dial-color: var(--dsg-orange); --dsg-bar-color: var(--dsg-orange); }
.dsg-tint-amber { --dsg-figure-dial-color: var(--dsg-amber); --dsg-bar-color: var(--dsg-amber); }
.dsg-tint-aqua { --dsg-figure-dial-color: var(--dsg-aqua); --dsg-bar-color: var(--dsg-aqua); }
.dsg-tint-teal { --dsg-figure-dial-color: var(--dsg-teal); --dsg-bar-color: var(--dsg-teal); }
.dsg-tint-ramp-1 { --dsg-figure-dial-color: var(--dsg-bars-ramp-1); --dsg-bar-color: var(--dsg-bars-ramp-1); }
.dsg-tint-ramp-2 { --dsg-figure-dial-color: var(--dsg-bars-ramp-2); --dsg-bar-color: var(--dsg-bars-ramp-2); }
.dsg-tint-ramp-3 { --dsg-figure-dial-color: var(--dsg-bars-ramp-3); --dsg-bar-color: var(--dsg-bars-ramp-3); }
.dsg-tint-ramp-4 { --dsg-figure-dial-color: var(--dsg-bars-ramp-4); --dsg-bar-color: var(--dsg-bars-ramp-4); }

/* The caption under the ring instead of beside it. A pair of dials sharing a
   row reads as two columns this way; with the caption beside each ring it
   reads as four. It has to sit after --dial to undo what --dial writes. */
.dsg-figure--stack {
  --dsg-figure-direction: column;
  --dsg-figure-align: center;
  --dsg-figure-text-align: center;
  --dsg-figure-width: auto;
}

/* Two figures sharing a row, and keeping it on a phone. Each half becomes a
   container of its own: a figure outside a card would otherwise measure its
   cqw sizes against the viewport and come out enormous. */
.dsg-figure-pair {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: var(--dsg-figure-pair-gap);
  width: min(100%, var(--dsg-figure-pair-width));
  margin-inline: auto;
}

.dsg-figure-pair > .dsg-figure {
  --dsg-figure-gap: var(--dsg-figure-pair-figure-gap);
  --dsg-figure-dial-size: var(--dsg-figure-pair-dial-size);
  --dsg-figure-dial-value-size: var(--dsg-figure-pair-value-size);
  --dsg-figure-caption-size: var(--dsg-figure-pair-caption-size);
  flex: 1 1 0;
  min-width: 0;
}

/* --- Bar chart -------------------------------------------------------------
   Bars that grow out of a shared baseline when the block scrolls into view,
   each with an icon riding on its tip and its number counting up underneath.
   There is no script for this: every bar is a figure, so the fraction the
   figure publishes to fill a ring is the same fraction that gives the bar its
   height, and the number under it is that figure's own value counting up. A
   bar is added by adding markup, and nothing here needs to know how many.

     .dsg-bars            the block
     .dsg-bars-plot       the row the bars stand in
     .dsg-bar             one bar, and the figure element
     .dsg-tint-*  which colour it takes, and nothing else
     .dsg-bar-track       the full height a bar could reach
     .dsg-bar-icon        the icon above the tip
     .dsg-bar-fill        the bar itself
     .dsg-bar-value       the counting number under the baseline
     .dsg-bars-caption    the one line under all of them

   The baseline is drawn once, by the block, so it runs unbroken across the
   gaps between the bars. Its distance from the top is the same token the
   tracks are tall, which is what keeps the two in step.
   ------------------------------------------------------------------------ */
.dsg-bars {
  --dsg-figure-value-size: var(--dsg-bars-value-size);
  --dsg-figure-caption-size: var(--dsg-bars-caption-size);
  --dsg-bars-fill-room: calc(
    var(--dsg-bars-height) - var(--dsg-bars-icon-size) - var(--dsg-bars-icon-gap)
  );
  position: relative;
  width: min(100%, var(--dsg-bars-width));
  margin-inline: auto;
}

.dsg-bars::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: var(--dsg-bars-height);
  border-block-start:
    var(--dsg-bars-rule-width) solid var(--dsg-bars-rule-color);
}

.dsg-bars-plot {
  display: flex;
  align-items: flex-start;
  gap: var(--dsg-bars-gap);
}

.dsg-bar {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

.dsg-bar-track {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  height: var(--dsg-bars-height);
}

/* The icon keeps a row of its own so that the room left for the bar is a
   number this file can work out. Its width still gives way to a narrow bar,
   which is what happens on a phone. */
.dsg-bar-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: min(var(--dsg-bars-icon-size), var(--dsg-bars-bar-width));
  height: var(--dsg-bars-icon-size);
  margin-block-end: var(--dsg-bars-icon-gap);
  color: var(--dsg-bar-color);
}

.dsg-bar-icon svg {
  width: 100%;
  height: 100%;
  fill: currentColor;
}

.dsg-bar-fill {
  width: var(--dsg-bars-bar-width);
  height: calc(var(--dsg-bars-fill-room) * var(--dsg-figure-dial-progress, 0));
  border-start-start-radius: var(--dsg-bars-bar-radius);
  border-start-end-radius: var(--dsg-bars-bar-radius);
  background: var(--dsg-bar-color);
}

.dsg-bar-value {
  margin-block-start: var(--dsg-bars-value-gap);
}

.dsg-bars-caption {
  margin-block-start: var(--dsg-bars-caption-gap);
  text-align: center;
}

/* A heading and a figure in a card scale with it too. Leaving them at their
   global size would break the promise the card makes: that a card at half
   the width is the same picture, only smaller. The eyebrow is not here
   because it reads --dsg-eyebrow-size, which the card writes above. */
.dsg-card-strip .elementor-widget-heading:not(.dsg-eyebrow) .elementor-heading-title {
  font-size: var(--dsg-card-strip-card-title-size);
  line-height: var(--dsg-card-strip-card-title-line-height);
}

.dsg-card-strip .elementor-counter-number-wrapper {
  font-size: var(--dsg-card-strip-card-figure-size);
  line-height: 1;
}

.dsg-card-strip .elementor-counter-title {
  font-size: var(--dsg-card-strip-card-text-size);
  line-height: var(--dsg-card-strip-card-line-height);
}


/* ============================================================================
 * 4  BOX  -  size and spacing only, never paint
 * ========================================================================= */

.dsg-panel {
  width: var(--dsg-panel-width);
  height: var(--dsg-panel-height, auto);
  min-height: var(--dsg-panel-min-height, 0);
  box-sizing: border-box;
  padding: var(--dsg-panel-padding);
  overflow: hidden;
  border-radius: var(--dsg-panel-radius);
}

/* Panel type: the card block. Heading plus card row, sized as one. */
.dsg-cards {
  min-height: var(--dsg-panel-cards-height);
}

/* Card block that carries more than one row of cards, so it cannot share the
   height of a single-row block. Sits on the block beside dsg-cards and
   rewrites the type's own height token, the same way dsg-panel--content-short
   does for a content panel. */
.dsg-cards--strip {
  --dsg-panel-cards-height: var(--dsg-panel-cards-strip-height);
}

/* Panel type: full-height content block. */
.dsg-panel--content {
  --dsg-panel-min-height: var(--dsg-panel-content-height);
}

/* Content block that does not fill the full content height. Sits on the
   panel beside dsg-panel--content and writes the type's own height token,
   so the height still has exactly one owner and no property is overridden. */
.dsg-panel--content-short {
  --dsg-panel-content-height: var(--dsg-panel-content-short-height);
}

/* Content block whose height is simply its content's. The type's height is
   a floor that keeps siblings level, and on a wide screen a block whose text
   stops wrapping can fall well under it, leaving the difference as empty room
   at the end. This writes the floor away for that one block. Use it knowing
   the block will then be shorter than its siblings, which is the trade. */
.dsg-panel--content-fit {
  --dsg-panel-content-height: 0px;
}

/* Panel type: the compact card used for the hero and the closing block.
   Both carry this class, so both are always the same size. */
.dsg-panel--panorama {
  --dsg-panel-width: var(--dsg-panorama-panel-width);
  --dsg-panel-min-height: var(--dsg-panel-panorama-height);
  --dsg-panel-padding: var(--dsg-panorama-panel-padding);
}

.dsg-card {
  min-width: 0;
  min-height: var(--dsg-card-min-height);
  box-sizing: border-box;
  padding: var(--dsg-card-padding);
  overflow: hidden;
  border-radius: var(--dsg-card-radius);
}

/* Small labelled square. Its own component, not a small card. */
.dsg-tile {
  min-width: 0;
  min-height: var(--dsg-tile-min-height);
  box-sizing: border-box;
  padding: var(--dsg-tile-padding);
  border: var(--dsg-tile-border-width) solid var(--dsg-tile-border-color);
  border-radius: var(--dsg-tile-radius);
  background: var(--dsg-tile-background);
}

.dsg-header {
  top: var(--dsg-header-top);
  left: 50%;
  z-index: 50;
  box-sizing: border-box;
  width: var(--dsg-header-width);
  min-height: var(--dsg-header-min-height);
  padding: var(--dsg-header-padding);
  border-radius: var(--dsg-header-radius);
  transform: translateX(-50%);
  /* Variables read by section 5. Surface owns position, border and paint. */
  --dsg-surface-position: fixed;
  --dsg-border-style: var(--dsg-border-ink);
  --dsg-glass-fill: var(--dsg-ink-glass);
  --dsg-glass-shadow: var(--dsg-box-shadow-header);
  --dsg-glass-blur: var(--dsg-header-blur);
  --dsg-glass-saturation: var(--dsg-header-saturation);
}


/* ============================================================================
 * 5  SURFACE  -  paint only, never size
 *
 * Add .dsg-surface to anything that should look like glass. The look is
 * assembled from variables, so a component or modifier can retune it without
 * ever overriding a property here.
 * ========================================================================= */

.dsg-surface {
  position: var(--dsg-surface-position, relative);
  border: 0;
  background: var(--dsg-glass-fill, var(--dsg-glass));
  box-shadow: var(--dsg-glass-shadow, var(--dsg-box-shadow-panel));
  backdrop-filter: var(--dsg-glass-filter);
  -webkit-backdrop-filter: var(--dsg-glass-filter);
}

/* The gradient ring. Drawn as a masked pseudo element so the radius follows. */
.dsg-surface::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 10;
  box-sizing: border-box;
  padding: var(--dsg-ring-width);
  border-radius: inherit;
  pointer-events: none;
  background: var(--dsg-border-style, var(--dsg-border-default));
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
}

/* Ring colour modifiers. Each writes one variable and nothing else. */
.dsg-outline-violet   { --dsg-border-style: var(--dsg-border-violet); }
.dsg-outline-magenta  { --dsg-border-style: var(--dsg-border-magenta); }
.dsg-outline-orange   { --dsg-border-style: var(--dsg-border-orange); }
.dsg-outline-panorama { --dsg-border-style: var(--dsg-border-panorama); }
.dsg-outline-card-1   { --dsg-border-style: var(--dsg-border-card-1); }
.dsg-outline-card-2   { --dsg-border-style: var(--dsg-border-card-2); }
.dsg-outline-card-3   { --dsg-border-style: var(--dsg-border-card-3); }
.dsg-outline-card-4   { --dsg-border-style: var(--dsg-border-card-4); }

/* ============================================================================
 * 6  TYPE AND BUTTONS  -  roles Elementor globals do not cover
 * ========================================================================= */

/* A heading that stands on the sky rather than on a panel. Put it on the
   block that holds the eyebrow and the title and both get the shadow; the
   four values it is made of are in section 1. */
.dsg-headline-shadow,
.dsg-headline-shadow .elementor-heading-title {
  text-shadow: var(--dsg-headline-shadow);
}

.dsg-eyebrow .elementor-heading-title {
  color: var(--dsg-eyebrow-color);
  font-size: var(--dsg-eyebrow-size);
  font-weight: var(--dsg-eyebrow-weight);
  line-height: var(--dsg-eyebrow-line-height);
  letter-spacing: var(--dsg-eyebrow-letter-spacing);
  text-transform: uppercase;
}

/* Decorative non-heading symbols only. Real headings use Elementor globals. */
.dsg-display div.elementor-heading-title {
  color: var(--dsg-text);
  font-size: var(--dsg-symbol-size);
  font-weight: var(--dsg-symbol-weight);
  line-height: var(--dsg-symbol-line-height);
}

/* Semantic markers. Their type comes from Elementor globals on purpose, so
   the team can retype the page without touching this file. Keep them: they
   are the hook for anything the globals cannot express later. */
.dsg-section-title,
.dsg-lead,
.dsg-button-row {
  min-width: 0;
}

.dsg-button .elementor-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--dsg-button-width);
  text-align: center;
}

.dsg-button .elementor-button-content-wrapper {
  display: flex;
  flex-direction: var(--dsg-button-content-flow);
  align-items: center;
  justify-content: center;
  gap: var(--dsg-button-icon-gap);
  text-align: center;
}

.dsg-button-primary .elementor-button {
  border-color: var(--dsg-button-primary-border);
  background: var(--dsg-button-primary-background);
  box-shadow: var(--dsg-box-shadow-panel);
}

.dsg-button-secondary .elementor-button {
  border-color: var(--dsg-button-secondary-border);
  background: var(--dsg-button-secondary-background);
  box-shadow: none;
}

/* The mark a button carries. Nothing is drawn until one of the classes below
   names a drawing: with no --dsg-button-icon-mark there is no content, and a
   pseudo-element without content is never generated at all. */
.dsg-button .elementor-button-content-wrapper::before {
  content: var(--dsg-button-icon-mark, none);
  flex: 0 0 auto;
  width: var(--dsg-button-icon-size);
  height: var(--dsg-button-icon-size);
  background: currentColor;
  -webkit-mask-image: var(--dsg-button-icon);
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  -webkit-mask-size: contain;
  mask-image: var(--dsg-button-icon);
  mask-repeat: no-repeat;
  mask-position: center;
  mask-size: contain;
}

/* A row of contact buttons. Every button carries the place it takes when the
   row breaks into two, and --dsg-button-order-mix decides whether that place
   counts at all: multiplied by 0 they are all 0 and the row reads in the
   order it is written. */
.dsg-contact-buttons > .dsg-button {
  order: calc(var(--dsg-button-order-narrow, 0) * var(--dsg-button-order-mix));
  flex: var(--dsg-button-flex);
}

/* One class per drawing. Each writes the variables above and nothing else,
   so any of them sits on top of any button variant. */
.dsg-button-tiktok {
  --dsg-button-order-narrow: 3;
  --dsg-button-icon-mark: "";
  --dsg-button-icon: url("../icons/tiktok.svg");
}

.dsg-button-instagram {
  --dsg-button-order-narrow: 1;
  --dsg-button-icon-mark: "";
  --dsg-button-icon: url("../icons/instagram.svg");
}

.dsg-button-youtube {
  --dsg-button-order-narrow: 2;
  --dsg-button-icon-mark: "";
  --dsg-button-icon: url("../icons/youtube.svg");
}

.dsg-button-discord {
  --dsg-button-order-narrow: 5;
  --dsg-button-icon-mark: "";
  --dsg-button-icon: url("../icons/discord.svg");
}

.dsg-button-x {
  --dsg-button-order-narrow: 4;
  --dsg-button-icon-mark: "";
  --dsg-button-icon: url("../icons/x.svg");
}

.dsg-button-mail {
  --dsg-button-order-narrow: 6;
  --dsg-button-icon-mark: "";
  --dsg-button-icon: url("../icons/mail.svg");
}

/* A line of small print whose links belong to the sentence rather than
   standing out of it. The theme gives every link a size and a colour of its
   own; the size is handed straight back, and the colour is a token. */
.dsg-legal a {
  font: inherit;
  color: var(--dsg-legal-link);
  text-decoration: none;
}

.dsg-legal a:hover,
.dsg-legal a:focus-visible {
  color: var(--dsg-legal-link-hover);
}

.dsg-button-nav .elementor-button {
  color: var(--dsg-button-nav-color);
  font-size: var(--dsg-button-nav-size);
  font-weight: var(--dsg-button-nav-weight);
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}

.dsg-button-nav .elementor-button:hover,
.dsg-button-nav .elementor-button:focus-visible {
  color: var(--dsg-button-nav-hover-color);
  border-color: var(--dsg-button-nav-hover-border);
  background: var(--dsg-button-nav-hover-background);
}

.dsg-button-compact .elementor-button {
  min-height: var(--dsg-nav-button-min-height);
  padding: var(--dsg-nav-button-padding);
  border-radius: var(--dsg-nav-button-radius);
}


/* ============================================================================
 * 7  DUO  -  two columns with an accent line, driven by grid areas
 *
 * Put .dsg-duo on the panel and one slot class on each child. Children are
 * placed by name, so the responsive order is a token swap in section 1
 * rather than a pile of order and display rules.
 *
 *   .dsg-duo-copy      heading and text, top left
 *   .dsg-duo-media     the block under the copy
 *   .dsg-divider       the accent line
 *   .dsg-duo-aside     the block on the right
 *   .dsg-duo-card      the block under the aside
 *
 * Use .dsg-duo--aside-tall when the right side is one tall element that
 * should span both rows instead of a pair.
 *
 * Use .dsg-duo--one-row when the left side is copy alone, so the block is
 * one row and an empty second row cannot add its gap to the height.
 *
 * A .dsg-duo panel does NOT carry .dsg-split: it defines its own grid, so
 * nothing overrides anything.
 * ========================================================================= */

.dsg-duo {
  display: grid;
  align-content: center;
  grid-template-columns: var(--dsg-duo-columns);
  grid-template-rows: var(--dsg-duo-rows);
  grid-template-areas: var(--dsg-duo-areas);
  column-gap: var(--dsg-duo-column-gap);
  row-gap: var(--dsg-duo-row-gap);
}

/* Right side is one tall element spanning both rows. */
.dsg-duo--aside-tall {
  --dsg-duo-areas: var(--dsg-duo-areas-aside-tall);
}

/* Left side is copy alone, so there is no second row for it to open. */
.dsg-duo--one-row {
  --dsg-duo-areas: var(--dsg-duo-areas-one-row);
  --dsg-duo-rows: auto;
}

/* Divider on the centre line instead of the default off-centre split. */
.dsg-duo--even {
  --dsg-duo-columns: var(--dsg-duo-columns-even);
}

/* Fill the panel instead of floating in the middle of it. Use this whenever
   the panel type is taller than the block needs, so the leftover height goes
   into the media row rather than into dead bands above and below. */
.dsg-duo--fill {
  --dsg-duo-rows: var(--dsg-duo-rows-fill);
}

/* The lower row carries the leftover height. Its blocks sit at the top of it
   instead of being stretched or centred, so any slack ends up at the bottom
   of the panel rather than as a band above and below the content. */
.dsg-duo--fill .dsg-duo-media,
.dsg-duo--fill .dsg-duo-card {
  align-self: start;
}

.dsg-duo-copy  { grid-area: copy;  min-width: 0; }
.dsg-duo-media { grid-area: media; min-width: 0; }
.dsg-duo-aside { grid-area: aside; min-width: 0; }
.dsg-duo-card  { grid-area: card;  min-width: 0; }

/* Media: one rounded crop for every kind of visual. Add .dsg-media to the
   image, video or embed widget. Pictures keep their own proportions: the
   rounded corner is the crop, the picture is never squeezed into a box it
   does not fit. */
.dsg-media img,
.dsg-media video,
.dsg-media iframe,
.dsg-media .elementor-wrapper {
  border-radius: var(--dsg-media-radius);
  overflow: hidden;
}

/* A self-hosted video that carries its own transparency and its own
   proportions: shown whole, never cropped, nothing painted behind it.
   These selectors carry Elementor's own widget classes on purpose. The video
   widget ships a black background and object-fit: cover at that exact
   specificity, and a shorter selector loses to them. */
.dsg-media--alpha .elementor-wrapper {
  aspect-ratio: auto;
  background: transparent;
}

.elementor-widget-video.dsg-media--alpha .elementor-wrapper video {
  position: static;
  width: 100%;
  height: auto;
  border: 0;
  background: transparent;
}

.dsg-media--alpha .e-hosted-video .elementor-video {
  object-fit: contain;
}

/* The picture standing in for that video. It sits exactly where the video sat
   and is sized by the same numbers, so nothing around it has to know which of
   the two it got. */
.dsg-media--alpha .dsg-alpha-fallback {
  display: block;
  position: static;
  width: 100%;
  height: auto;
  border: 0;
  background: transparent;
  object-fit: contain;
}

/* The accent line. Hidden by the token on stacked layouts. */
.dsg-divider {
  grid-area: divider;
  display: var(--dsg-divider-display);
  align-self: center;
  justify-self: center;
  width: var(--dsg-divider-width);
  min-width: var(--dsg-divider-width);
  height: var(--dsg-divider-height);
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--dsg-divider-radius);
  background: linear-gradient(
    180deg,
    var(--dsg-divider-color-top),
    var(--dsg-divider-color-middle) 58%,
    var(--dsg-divider-color-bottom)
  );
  box-shadow: 0 0 var(--dsg-divider-glow-size) var(--dsg-divider-glow-color);
  overflow: visible;
  z-index: 2;
}

/* Elementor owns the row: direction, wrap, gap and padding are set on the
   container in the editor. The system only decides how the two buttons
   share the space, and that is a token. The child selector matches
   Elementor's own .e-con > .elementor-widget specificity, which is why it
   works without !important. */
.dsg-hero-buttons > .dsg-hero-button-primary-item {
  flex: var(--dsg-hero-button-primary-flex);
  width: auto;
  min-width: 0;
}

.dsg-hero-buttons > .dsg-hero-button-secondary-item {
  flex: var(--dsg-hero-button-secondary-flex);
  width: auto;
  min-width: 0;
}

/* Only where the two buttons share the row. On desktop they keep the
   Elementor button baseline untouched. */
@media (max-width: 1024px) {
  .dsg-hero-buttons .elementor-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding-inline: clamp(10px, 2vw, 22px);
    line-height: 1.2;
    text-align: center;
  }
}

/* Below Elementor's smallest breakpoint the two labels stop fitting.
   This is the one place the system overrides an Elementor value, because
   Elementor has no breakpoint here. */
@media (max-width: 480px) {
  .dsg-hero-buttons {
    gap: var(--dsg-hero-buttons-gap-small);
  }

  .dsg-hero-buttons .elementor-button {
    padding: 12px 6px;
    font-size: clamp(0.62rem, 3vw, 0.78rem);
    white-space: nowrap;
  }
}


/* ============================================================================
 * 8  BRIDGE  -  Essential Addons menu markup
 *
 * This is the only place that reaches into markup we do not own. The widget
 * renders a fixed structure, so the selectors have to name it. The chains are
 * long on purpose: the widget ships its own per-element rules at that exact
 * specificity, and a shorter selector silently loses to them. Everything the
 * rules set still comes from tokens in section 1.
 * ========================================================================= */

@media (max-width: 1024px) {
  .dsg-nav-links {
    display: none;
  }

  .dsg-header-brand {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
    max-width: var(--dsg-header-brand-max-width);
  }

  .dsg-responsive-menu {
    position: static;
    z-index: 20;
    width: var(--dsg-responsive-menu-toggle-size);
    margin-left: auto;
  }

  /* The widget preset paints the container purple and leaves a strip of it
     showing under the toggle, because the toggle is inline. Both go here:
     the chain carries the widget class so it outranks that preset, for the
     same reason as every other selector in this section. */
  .dsg-responsive-menu.elementor-widget-eael-simple-menu .eael-simple-menu-container {
    position: static;
    display: flex;
    width: auto;
    background: transparent;
  }

  .dsg-responsive-menu .eael-simple-menu-container .eael-simple-menu-toggle {
    position: static;
    display: grid;
    width: var(--dsg-responsive-menu-toggle-size);
    height: var(--dsg-responsive-menu-toggle-size);
    min-height: var(--dsg-responsive-menu-toggle-size);
    padding: 0;
    place-items: center;
    color: var(--e-global-color-primary);
    border: 1px solid var(--dsg-button-primary-border);
    border-radius: var(--dsg-nav-button-radius);
    background: var(--dsg-button-primary-background);
    box-shadow: none;
  }

  .dsg-responsive-menu .eael-simple-menu-container .eael-simple-menu-toggle svg {
    width: var(--dsg-responsive-menu-icon-size);
    height: var(--dsg-responsive-menu-icon-size);
    fill: currentColor;
  }

  .dsg-responsive-menu .eael-simple-menu-container .eael-simple-menu {
    position: absolute;
    top: calc(100% + var(--dsg-responsive-menu-offset));
    right: 0;
    left: auto;
    z-index: 60;
    box-sizing: border-box;
    width: 100%;
    max-width: var(--dsg-responsive-menu-width);
    margin: 0;
    padding: var(--dsg-responsive-menu-padding);
    list-style: none;
    border: var(--dsg-ring-width) solid transparent;
    border-radius: var(--dsg-header-radius);
    background:
      var(--dsg-ink-glass) padding-box,
      var(--dsg-border-ink) border-box;
    box-shadow: var(--dsg-box-shadow-header);
    backdrop-filter: var(--dsg-header-filter);
    -webkit-backdrop-filter: var(--dsg-header-filter);
  }

  .dsg-responsive-menu .eael-simple-menu-container .eael-simple-menu > .menu-item {
    width: 100%;
    margin: 0;
  }

  .dsg-responsive-menu .eael-simple-menu-container .eael-simple-menu > .menu-item + .menu-item {
    margin-top: var(--dsg-responsive-menu-gap);
  }

  /* The widget ships display: block on this link through a chain of six
     classes, which is why this one carries seven. Without it the label sits
     on a line box shorter than the button and reads as too high. */
  .dsg-responsive-menu.elementor-widget-eael-simple-menu .eael-simple-menu-container.eael-simple-menu-hamburger .eael-simple-menu > .menu-item > .menu-link {
    display: flex;
    min-height: var(--dsg-nav-button-min-height);
    padding: var(--dsg-nav-button-padding);
    align-items: center;
    color: var(--dsg-button-nav-color);
    font-size: var(--dsg-button-nav-size);
    font-weight: var(--dsg-button-nav-weight);
    line-height: 1;
    border: 1px solid transparent;
    border-radius: var(--dsg-nav-button-radius);
    background: transparent;
  }

  .dsg-responsive-menu.elementor-widget-eael-simple-menu .eael-simple-menu-container.eael-simple-menu-hamburger .eael-simple-menu > .menu-item > .menu-link:hover,
  .dsg-responsive-menu.elementor-widget-eael-simple-menu .eael-simple-menu-container.eael-simple-menu-hamburger .eael-simple-menu > .menu-item > .menu-link:focus-visible {
    color: var(--dsg-button-nav-hover-color);
    border-color: var(--dsg-button-nav-hover-border);
    background: var(--dsg-button-nav-hover-background);
  }

  .dsg-responsive-menu.elementor-widget-eael-simple-menu .eael-simple-menu-container.eael-simple-menu-hamburger .eael-simple-menu > .menu-item:last-child > .menu-link {
    color: var(--e-global-color-primary);
    border-color: var(--dsg-button-primary-border);
    background: var(--dsg-button-primary-background);
  }
}


/* ============================================================================
 * 9  REUSABLE SVG HOOKS  -  driven by the plugin JS via data attributes
 * ========================================================================= */

[data-dsg-progress] { stroke-dasharray: 0 100; }
[data-dsg-bar]      { height: var(--dsg-bar-value, 0%); }

