/* =====================================================================
   The ONLY stylesheet in this project that is not the original site's.

   Everything the visitor sees is styled by ds-site.css — their own CSS,
   scoped to `.ds-app` and repainted in Dokie's palette. This file holds the
   four things their stylesheet could not know about, and nothing else. Every
   rule is listed here with the reason it exists; if a rule ever needs adding
   that is not on this list, it belongs in the palette step instead.

     1. the mobile submenu, which their jQuery opened with an inline style
     2. the loading skeleton, which a static site has no concept of
     3. the Author's in-place edit affordances, which a static site has no
        concept of either — and which must be invisible to a visitor
     4. two defences against the platform shell
   ===================================================================== */

/* -- 1 -------------------------------------------------------------------
   Below 992px their stylesheet makes `.dropdown-menu` static but never undoes
   the `opacity:0; visibility:hidden` it set for the desktop hover, because
   jQuery's .toggle() wrote an inline style over it. We open it with a class
   so the transition survives and nothing is written inline. */
@media (max-width: 991px) {
  .ds-app .header-section .navbar .navbar-nav .dropdown > .dropdown-menu,
  .ds-app .header-section .navbar .navbar-nav .dropdown > .drop_menu {
    display: none;
    opacity: 1;
    visibility: visible;
    transform: none;
  }
  .ds-app .header-section .navbar .navbar-nav .dropdown.is-open > .dropdown-menu,
  .ds-app .header-section .navbar .navbar-nav .dropdown.is-open > .drop_menu {
    display: block;
    animation: ds-drop .25s ease both;
  }
}
@keyframes ds-drop { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* ⛔ THE LANGUAGE SWITCH — see §26. Its rules live there, with the rest of the
   header cluster, so the three controls cannot drift apart again. */

/* -- 2 -------------------------------------------------------------------
   The skeleton. The page's words arrive over one bridge call, so there is a
   moment with nothing to show; a grey pulse reads as loading, a blank white
   screen reads as broken. */
.ds-app .ds-skeleton { display: grid; gap: 14px; padding: 120px 0; }
.ds-app .ds-skel { border-radius: 8px; background: rgba(var(--p4), .12); animation: ds-pulse 1.4s ease-in-out infinite; }
.ds-app .ds-skel--title { height: 42px; max-width: 520px; }
.ds-app .ds-skel--line  { height: 16px; }
.ds-app .ds-skel--block { height: 280px; border-radius: 16px; }
@keyframes ds-pulse { 0%,100% { opacity: .55; } 50% { opacity: .2; } }

/* -- 3 -------------------------------------------------------------------
   The Author's affordances. `.ds-authoronly` is display:none by default and is
   only ever un-hidden by the component after the rule has said this visitor may
   edit — so a CSS-only inspection of a visitor's page shows nothing to click. */
.ds-app .ds-authoronly { display: none; }
.ds-app.ds-can-edit .ds-authoronly { display: block; }
.ds-app .ds-blockwrap { position: relative; }
.ds-app.ds-can-edit .ds-blockwrap:hover { outline: 1px dashed rgba(var(--p1), .5); outline-offset: -1px; }
.ds-app .ds-editpin { position: absolute; top: 14px; right: 14px; z-index: 12; }
.ds-app .ds-editpin__btn {
  display: inline-flex; gap: 7px; align-items: center; padding: 7px 14px; border-radius: 999px;
  font-size: 13px; font-weight: 600; cursor: pointer;
  color: rgba(var(--p6), 1); background: rgba(var(--p1), .95); border: 0;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
}
.ds-app .ds-editpin__btn:hover { background: rgba(var(--p1), 1); }

/* the cookie strip: their card, pinned */
.ds-app .ds-cookie {
  position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 60; max-width: 760px;
  margin-inline: auto; background: rgba(var(--p5), .98); border: 1px solid rgba(var(--p4), .16);
  box-shadow: var(--shadow-lg);
  transform: translateY(140%); transition: transform .45s cubic-bezier(.2, .8, .2, 1);
}
.ds-app .ds-cookie.is-open { transform: none; }

/* -- 4 -------------------------------------------------------------------
   Two defences against the shell.
   `overflow-x: clip` — not hidden: hidden on an ancestor would break the
   sticky header, clip does not. Their off-canvas drawer is translated 100% off
   to the right and would otherwise stretch the document to twice its width.
   `contain: layout` on the app root keeps our `position: fixed` children
   measured against the viewport rather than against a platform panel. */
.ds-app { overflow-x: clip; }
.ds-app .ds-noswiper .swiper-slide:not(:first-child) { display: none; }
.ds-app .ds-noswiper .swiper-wrapper { display: block; }

/* -- 5 -------------------------------------------------------------------
   The contact form. The original ships one — commented out, because a static
   site had nowhere to send it; that is the very thing the brief asked to make
   real. So it has markup (`.form-control p-4 rounded-3`) but no theme of its
   own, and bootstrap's default is a white box that looks pasted onto the dark
   skin. These rules give it their surface, their border and their brand focus
   ring, using only the palette variables. */
.ds-app .form-control,
.ds-app .single-input input {
  background: rgba(var(--p7), .6);
  border: 1px solid rgba(var(--p4), .2);
  color: rgba(var(--p8), 1);
  transition: border-color .25s, box-shadow .25s, background .25s;
}
.ds-app .form-control::placeholder,
.ds-app .single-input input::placeholder { color: rgba(var(--p4), .8); }
.ds-app .form-control:focus,
.ds-app .single-input input:focus {
  background: rgba(var(--p7), .9);
  border-color: rgba(var(--p1), .7);
  box-shadow: 0 0 0 4px rgba(var(--p1), .16);
  color: rgba(var(--p8), 1);
  outline: 0;
}
.ds-app .form-control:-webkit-autofill { -webkit-text-fill-color: rgba(var(--p8), 1); }
.ds-app .ds-field.is-invalid .form-control { border-color: rgba(var(--p3), .8); }
.ds-app select.form-control { appearance: none; padding-right: 44px;
  background-image: linear-gradient(45deg, transparent 50%, rgba(var(--p4), 1) 50%),
                    linear-gradient(135deg, rgba(var(--p4), 1) 50%, transparent 50%);
  background-position: calc(100% - 22px) calc(50% + 2px), calc(100% - 16px) calc(50% + 2px);
  background-size: 6px 6px, 6px 6px; background-repeat: no-repeat; }
/* the honeypot is off-screen, never display:none — a bot reads the computed style */
.ds-app .ds-hp { position: absolute !important; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* -- 6 -------------------------------------------------------------------
   The comparison panel and the step number, both assembled from their own
   parts (bordered panel, brand accent) because the original has no section of
   either kind to copy. */
.ds-app .comparison-row + .comparison-row { border-top: 1px solid rgba(var(--p4), .12); }
.ds-app .comparison-head { background: rgba(var(--p7), .7); }
.ds-app .comparison-row > [class^="col"] { display: flex; flex-direction: column; justify-content: center; }
.ds-app .step-card .step-num { line-height: 1; opacity: .38; }
.ds-app .pricing_section--featured > div { border-color: rgba(var(--p1), .65) !important; }
.ds-app .price_badge { position: absolute; top: -14px; left: 50%; transform: translateX(-50%); z-index: 2; }
.ds-app .ds-adminnav > button { color: rgba(var(--p2), 1) !important; }

/* -- 7 -------------------------------------------------------------------
   The Author's in-place editor. Author-only, so it is deliberately plain: the
   site's own type scale and surfaces, no separate visual language to maintain,
   and nothing that could be mistaken for part of the page by a visitor. */
.ds-app .ds-editor {
  position: fixed; inset: auto 0 0 0; z-index: 70; max-height: 78vh; overflow: auto;
  background: rgba(var(--p5), .99); border-top: 1px solid rgba(var(--p4), .2);
  box-shadow: 0 -18px 60px rgba(0, 0, 0, .3);
  transform: translateY(100%); transition: transform .4s cubic-bezier(.2, .8, .2, 1);
}
.ds-app .ds-editor.is-open { transform: none; }
.ds-app .ds-editor__head,
.ds-app .ds-editor__foot {
  display: flex; gap: 12px; align-items: center; padding: 14px 22px;
  color: rgba(var(--p8), 1); font-size: 14px; font-weight: 600;
  border-bottom: 1px solid rgba(var(--p4), .14);
}
.ds-app .ds-editor__foot { border-bottom: 0; border-top: 1px solid rgba(var(--p4), .14); justify-content: flex-end; }
.ds-app .ds-editor__title { color: rgba(var(--p4), 1); font-weight: 500; }
.ds-app .ds-editor__body { padding: 20px 22px; }
.ds-app .ds-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 18px; }
.ds-app .ds-tabs button {
  padding: 7px 16px; border-radius: 999px; border: 1px solid rgba(var(--p4), .22);
  background: none; color: rgba(var(--p4), 1); font-size: 13px; font-weight: 600; cursor: pointer;
}
.ds-app .ds-tabs button[aria-selected="true"] {
  background: rgba(var(--p1), .16); border-color: rgba(var(--p1), .5); color: rgba(var(--p8), 1);
}
.ds-app .ds-editdock {
  position: fixed; right: 18px; bottom: 18px; z-index: 65;
  display: flex; gap: 8px; align-items: center; padding: 8px 10px 8px 14px; border-radius: 999px;
  font-size: 13px; font-weight: 600; color: rgba(var(--p8), 1);
  background: rgba(var(--p5), .97); border: 1px solid rgba(var(--p4), .2);
  box-shadow: 0 10px 34px rgba(0, 0, 0, .26);
}

/* -- 8 -------------------------------------------------------------------
   WHAT THE ADAPTATION LOST, AND WHAT IS LEFT HERE.

   The DevSegment site is an adaptation of the Technoxit template, and the
   pristine template sits on disk beside it. Comparing them showed that the
   adaptation kept the whole stylesheet and stopped using most of it: it
   invented `.service-card`, `.counter-box`, `.industry-card`,
   `.service-home-card`, `.audit-feature` and `.read-more`, and no stylesheet
   anywhere defines any of them. That is why those sections render as
   unsurfaced text floating on a flat band on the live original.

   An earlier pass here answered that by inventing surfaces for those class
   names. That was the wrong answer: the template's OWN components —
   `.single-item`, `.number-area`, `.thump-img-area`, `.process_icon_bg`,
   `.process_icon_num`, `.counter_right_side`, `.choose_us`,
   `.choose_icon_width`, `.dicscuss_projects` — are all fully designed, and all
   of their rules are already in the bundle we ship. So the renderers emit those
   instead, and every rule that used to be here is gone.

   What remains is only the handful of boxes the template genuinely has no
   component for: the comparison panel, the contact card and the form panel. */
.ds-app .contact-card,
.ds-app .contact-form-box,
.ds-app .comparison-table {
  background: linear-gradient(180deg, rgba(var(--p7), .85) 0%, rgba(var(--p7), .55) 100%);
  border: 1px solid rgba(var(--p4), .14);
  transition: transform .35s cubic-bezier(.2, .8, .2, 1), border-color .35s, box-shadow .35s;
}
.ds-app .contact-card:hover {
  transform: translateY(-6px);
  border-color: rgba(var(--p1), .45);
  box-shadow: 0 22px 50px -24px rgba(var(--p9), .55);
}
.ds-app .contact-card .icon-box {
  width: 56px; height: 56px; flex: 0 0 56px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(var(--p1), .12); border: 1px solid rgba(var(--p1), .22); border-radius: 50%;
}

/* "Learn more ->" — their markup, with the arrow that leans in on hover */
.ds-app .read-more { text-decoration: none; width: max-content; }
.ds-app .read-more .ph { transition: transform .3s ease; }
.ds-app .read-more:hover .ph { transform: translateX(5px); }

/* the FAQ card: `.faq` carries the border utilities but no resting surface */
.ds-app .faq { border-radius: 14px; background: rgba(var(--p7), .5); transition: background .3s, border-color .3s; }
.ds-app .faq:hover { border-color: rgba(var(--p1), .4) !important; }
.ds-app .faq.faqactive { background: rgba(var(--p7), .85); border-color: rgba(var(--p1), .45) !important; }
.ds-app .faq .answer { max-height: 0; }

/* their card icon plate holds a webfont glyph here, not a 60px PNG */
.ds-app .single-item .img-area,
.ds-app .process_icon_bg > .ph {
  display: inline-flex; align-items: center; justify-content: center; line-height: 1;
}
.ds-app .single-item .img-area {
  width: 56px; height: 56px;
  background: rgba(var(--p1), .12); border: 1px solid rgba(var(--p1), .22); border-radius: 14px;
}
.ds-app .single-item .thump-img-area img { width: 100%; object-fit: cover; }

/* the hero's chips read as pills only if they have room to breathe */
.ds-app .banner-section.index-one .marquee-section .item-area { backdrop-filter: blur(6px); }
.ds-app .banner-section.index-one .marquee-section .img-area .ph { line-height: 1; }

/* -- 9 -------------------------------------------------------------------
   The hero's living field. One canvas for the whole carousel, laid over the
   slide's screenshot (z-index 0) and under the slide's copy (z-index 2), so it
   reads as depth rather than as an overlay. `pointer-events: none` because the
   pointer is read off the section — the canvas must never eat a click on the
   CTA sitting above it. Both skins are handled by the canvas itself: it inks
   from --p1/--p2 and re-inks when the theme toggles. */
.ds-app .banner-translate-carousel > canvas.ds-ainet {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%;
  pointer-events: none;
  opacity: .95;
}
/* below lg their hero stacks and the copy gets tight; the field steps back so it
   never competes with a headline that now has less room around it */
@media (max-width: 991px) {
  .ds-app .banner-translate-carousel > canvas.ds-ainet { opacity: .5; }
}

/* -- 10 ------------------------------------------------------------------
   THE STANDARD (LIGHT) SKIN.

   The original ships both skins but was only ever finished in the dark one:
   three things are painted white with nothing behind them, so on the light
   skin they are white on white and simply disappear. Each is listed with what
   goes wrong, because each is a real defect and not a taste call.

   (a) `.dark_btn` — the theme toggle, the drawer button and our language
       switcher. The class sets no background at all and its contents carry
       `p6-color` (white). Invisible on a white header. */
.ds-app .header-section .dark_btn,
.ds-app .ds-lang-btn {
  width: 44px; height: 44px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(var(--p1), .10);
  border: 1px solid rgba(var(--p1), .20);
  color: rgba(var(--p8), 1);
  transition: background .25s, border-color .25s, transform .25s;
}

.ds-app .header-section .dark_btn:hover,
.ds-app .ds-lang-btn:hover { background: rgba(var(--p1), .18); border-color: rgba(var(--p1), .38); }
.ds-app .header-section .dark_btn > i,
.ds-app .header-section .dark_btn > span,
.ds-app .header-section .dark_btn > span > i,
.ds-app .ds-lang-btn > span { color: rgba(var(--p8), 1); }

/* (b) the hero's scrim. It is one gradient authored in the renderer, but the
       two skins need different weights: on the dark skin the screenshot may
       stay fairly present, on the light one an unblended dark screenshot behind
       white space reads as a hole. The four stops are variables so a single
       gradient serves both. */
/* ⛔ THE SHOT HAS TO BE SEEN, FAINTLY, ALL THE WAY ACROSS. The reference the
   owner pointed at (devsegment.com) puts a real product screen behind the
   headline: a table, dates, a calendar, readable as SHAPE and never as content.
   Ours was veiled to .97 under the copy, which is not faint — it is absent. The
   left stop now lets the shot through just enough to be felt behind the display
   type without touching its crispness, and the right stop opens up so the screen
   reads as a screen. Light needs more veil than dark for the same effect: a dark
   screenshot under a near-white ground goes to a hole long before it goes faint. */
/* The reference puts the screen BEHIND THE HEADLINE and keeps a subject on the
   right; ours has `svg.ds-art` as that subject, so the weight to remove is on the
   LEFT. The display type is 96px and bold — it stays crisp over a screen at this
   strength, and the lead paragraph sits inside the second stop where the veil is
   still heavy enough to carry 15px text. */
/* ⛔ THE LIGHT VEIL HAD TO COME DOWN ONCE THE PAGE STEPPED DOWN (§45). These
   weights were set against a page whose floor was 251,252,254: a light
   screenshot under a .56 light veil on a white ground is three near-whites
   stacked, and the product screen behind the home headline simply was not
   there. The dark theme never had that problem — a dark screenshot under a
   dark veil keeps all of its internal contrast, which is exactly why the
   owner reads that theme as the better one. With the ground now at
   240,244,250 the scrim has something to be darker THAN, so it can be
   thinner and still hold the display type. */
:root, .ds-app { --ds-hero-1: .46; --ds-hero-2: .42; --ds-hero-3: .27; --ds-hero-4: .15; }
/* ⛔ AND DARK NEEDS LESS VEIL, NOT MORE — THE OPPOSITE OF THE OLD TUNING. In
   dark the shot is the `-dark` twin: a dark UI under a dark scrim disappears
   long before a light UI under a light one does. The reference is a dark page
   and its screen is clearly perceptible, so these stops sit well below the light
   ones. White display type on a dark screen keeps its contrast easily. */
.ds-app.dark            { --ds-hero-1: .50; --ds-hero-2: .47; --ds-hero-3: .28; --ds-hero-4: .14; }

/* (c) the off-canvas panel and the slider arrows use `.n1-color`, which no
       stylesheet in the original defines — so those words fall through to the
       base text colour. That is now theme-aware, which is what makes them
       legible on both; this only pins the arrow glyphs, which sit on the
       hero's navy diagonal in BOTH skins and so are white in both. */
.ds-app .banner-section .slide-button .n1-color { color: rgba(255, 255, 255, .92); }
.ds-app .banner-section .slider-current,
.ds-app .banner-section .slider-total,
.ds-app .banner-section .slider-pagination-divider { color: rgba(var(--p8), 1); }

/* (d) the living field's strength. The alpha that reads as a whisper over a
       near-black hero is simply not there over a white one, so the standard
       skin scales every stroke of the canvas up by one number the script
       reads from here. */
/* ⛔ AND THE MOTIF STEPS BACK, BECAUSE IT WAS THE THING HIDING THE SHOT. The
   living field is drawn OVER the screenshot; at full strength it owned the right
   half of the band and the screen behind it could not be seen at all — which is
   why the hero read as abstract where the reference reads as software. It stays,
   because it is what makes the band feel alive, but as depth over a picture
   rather than as the picture. */
:root, .ds-app { --ds-field-k: 0.45; }
.ds-app.dark    { --ds-field-k: 0.52; }

/* -- 11 ------------------------------------------------------------------
   THE BLINDS — `.hero-shape`, the row of vertical glass slats on the right of
   the original's banner, the ones whose gap closes as a slide becomes active.

   Their own rule puts them at `z-index: -2`, which worked on the original
   because nothing was painted behind them; here the slide's product screenshot
   sits at 0, so at -2 they were invisible. Raising them was the obvious fix and
   the wrong one: they are `height: 124%` with a `translateY(-10%)`, so they hang
   below the carousel and, once raised, they frosted the brand chips underneath.

   ⛔ So the CAROUSEL gets its own stacking context instead, and the hero's whole
   z-order lives inside it:

        copy 3  ·  field 2  ·  blinds 1  ·  screenshot 0

   Everything after it in the section — the diagonal, the counter, the chip strip
   — paints above the lot, whatever the slats do inside. */
.ds-app .banner-section.index-one .banner-translate-carousel {
  isolation: isolate;
}
.ds-app .banner-section.index-one .hero-shape {
  z-index: 1 !important;
  transition: gap 1.4s ease, opacity .9s ease;
}
.ds-app .banner-translate-carousel > canvas.ds-ainet { z-index: 2; }
/* below lg their own markup drops the last three slats and the copy takes the
   full width, so the remaining ones would sit on the headline */
@media (max-width: 991px) {
  .ds-app .banner-section.index-one .hero-shape { display: none !important; }
}

/* -- 12 ------------------------------------------------------------------
   THE LEFT RAIL. Their markup gives this column a flat surface and four filled
   circles floating in the middle of it; these rules thread the icons onto a
   vertical rule, set the label along it, and hand the foot of the column a
   scroll cue — so 240px of hero stops being a grey slab. Nothing here changes
   the column's size, position or grid: it is what goes INSIDE it. */
.ds-app .banner-section.index-one .sidebar-left {
  background: linear-gradient(180deg, rgba(var(--p7), .40) 0%, rgba(var(--p7), .10) 100%);
  border-right: 1px solid rgba(var(--p4), .12);
}
/* ⛔ On the light skin this wash became a SLAB. It is painted in `--p7`, the same
   token as the tinted band, and §23 deepened that token so the bands would read —
   which pushed a 240px column of the hero ten points away from the page and gave
   it a hard right edge. The hairline is the deliberate thing here; the fill is
   only a hint that the column exists, so on light it is barely a breath. */
.ds-app:not(.dark) .banner-section.index-one .sidebar-left {
  background: linear-gradient(180deg, rgba(var(--p7), .26) 0%, rgba(var(--p7), .04) 100%);
}
/* ⛔ Centred in a 240px column, a 42px rail is a thin thing lost in a wide box.
   Hung against the divider instead, the empty left of the slab becomes deliberate
   negative space and the rail reads as attached to the edge it belongs to. */
.ds-app .banner-section.index-one .sidebar-left .social_info { justify-content: flex-end !important; }
/* ⛔ NO `margin-right` ANY MORE, AND IT WAS MY OWN RULE FIGHTING MY OWN RULE.
   `margin-right: clamp(18px, 2.2vw, 40px)` was written while this rail was a
   transparent slab: nudging the strip toward the hero was the only way to give
   it a relationship to anything. §42 then gave the rail a PAINTED panel, and the
   same margin became a 42px strip pinned to the right edge of a 323px rectangle
   with 240px of empty grid beside it — measured on the live home page. A ground
   changes what every offset on top of it means. */
.ds-app .ds-rail {
  height: 100%; justify-content: center; gap: 18px; padding: 40px 0;
}
.ds-app .ds-rail__line {
  width: 1px; flex: 1 1 auto; min-height: 26px;
  background: linear-gradient(180deg, rgba(var(--p4), 0) 0%, rgba(var(--p4), .34) 50%, rgba(var(--p4), 0) 100%);
}
.ds-app .ds-rail__line--short { flex: 0 0 34px; }
.ds-app .ds-rail__label {
  writing-mode: vertical-rl; text-orientation: mixed;
  font-size: 11px; font-weight: 600; letter-spacing: .28em; text-transform: uppercase;
  color: rgba(var(--p4), .8); white-space: nowrap; user-select: none;
}
.ds-app .ds-rail__icons { flex: 0 0 auto; }
.ds-app .banner-section.index-one .ds-rail__icon {
  width: 42px; height: 42px; border-radius: 50%;
  background: transparent !important;
  border: 1px solid rgba(var(--p4), .28);
  color: rgba(var(--p8), .82);
  transition: background .3s, border-color .3s, color .3s, transform .3s;
}
.ds-app .banner-section.index-one .ds-rail__icon i { color: inherit !important; }
.ds-app .banner-section.index-one .ds-rail__icon:hover {
  background: rgba(var(--p1), 1) !important;
  border-color: rgba(var(--p1), 1);
  color: #fff;
  transform: translateY(-2px);
}
.ds-app .ds-rail__scroll {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; flex: 0 0 auto;
  border: 1px solid rgba(var(--p4), .22); color: rgba(var(--p4), .9); font-size: 13px;
  animation: ds-rail-cue 2.4s ease-in-out infinite;
}
@keyframes ds-rail-cue {
  0%, 100% { transform: translateY(0); opacity: .6; }
  50%      { transform: translateY(6px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .ds-app .ds-rail__scroll { animation: none; } }

/* the slats read as glass only if there is a little light on their leading edge;
   their own gradient is a flat wash, so one hairline is added to each */
.ds-app .banner-section.index-one .hero-shape span {
  border-left: 1px solid rgba(var(--p8), .05);
  box-shadow: inset 1px 0 0 rgba(var(--p8), .04);
}

/* -- 13 ------------------------------------------------------------------
   THE SECTOR CHIPS under the hero's diagonal. The template's plate is a white
   pill for a client logo; these rows are sectors, not clients, so the plate
   takes the surface colour and the chip carries an icon and a label. A row with
   a real logo still gets the logo — see brandChip(). */
.ds-app .banner-section.index-one .marquee-section .item-area {
  min-width: 170px;
  backdrop-filter: blur(6px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
  transition: transform .3s ease, border-color .3s ease, box-shadow .3s ease;
}
.ds-app .banner-section.index-one .marquee-section a:hover .item-area {
  transform: translateY(-3px);
  border-color: rgba(var(--p1), .55) !important;
  box-shadow: 0 10px 26px rgba(0, 0, 0, .26);
}
.ds-app .banner-section.index-one .marquee-section .img-area img {
  max-height: 28px; width: auto; object-fit: contain;
}
.ds-app .banner-section.index-one .marquee-section .img-area .ph { line-height: 1; }

/* =====================================================================
   -- 14 --  DEPTH
   The original's sections are flat fills: one colour per band, a hard join
   between them, and cards with no surface at all. Flat dark does not read as
   expensive, it reads as unfinished. Five moves, all of them built from the
   palette variables so both skins get them for free and neither needs a second
   set of colours:

     (a) every band is a vertical GRADIENT rather than a fill;
     (b) every band carries one very large, very soft brand BLOOM, alternating
         left and right down the page, so the eye is led and no two bands look
         identical;
     (c) a GRAIN at 2.5% over everything — the single thing that stops a large
         dark gradient banding into stripes on an 8-bit panel, and the reason
         expensive dark sites look like paper and cheap ones look like plastic;
     (d) section joins get a hairline that fades out at both ends, instead of a
         hard colour step;
     (e) cards get a 1px inner highlight along their top edge — the thing that
         makes a surface read as lit rather than as painted.
   ===================================================================== */

/* (a) + (b) ---------------------------------------------------------------- */
.ds-app .ds-blockwrap { position: relative; isolation: isolate; overflow: hidden; }
.ds-app .ds-blockwrap > .container,
.ds-app .ds-blockwrap > .container-fluid { position: relative; z-index: 1; }

.ds-app .counter-section,
.ds-app .service-section,
.ds-app .services-home,
.ds-app .industries-section,
.ds-app .choose-section,
.ds-app .forms-section,
.ds-app .faq-section,
.ds-app .pricing-section,
.ds-app .comparison-section,
.ds-app .contact-section,
.ds-app .contact-form-section,
.ds-app .richtext-section,
.ds-app .cta-section {
  background-image: linear-gradient(180deg, rgba(var(--p7), .0) 0%, rgba(var(--p7), .55) 50%, rgba(var(--p7), .0) 100%);
}
.ds-app .bg7-color.ds-blockwrap {
  background-image: linear-gradient(180deg, rgba(var(--p7), .95) 0%, rgba(var(--p7), .55) 60%, rgba(var(--p7), .95) 100%);
}
/* the bloom: one per band, alternating side, and never strong enough to notice */
.ds-app .ds-blockwrap::before {
  content: ""; position: absolute; z-index: 0; pointer-events: none;
  width: 62vw; height: 62vw; max-width: 900px; max-height: 900px;
  top: -18%; right: -14%;
  background: radial-gradient(circle at center,
    rgba(var(--p1), .16) 0%, rgba(var(--p1), .06) 38%, rgba(var(--p1), 0) 70%);
}
.ds-app .ds-blockwrap:nth-of-type(even)::before {
  right: auto; left: -14%; top: auto; bottom: -18%;
  background: radial-gradient(circle at center,
    rgba(var(--p2), .13) 0%, rgba(var(--p2), .05) 38%, rgba(var(--p2), 0) 70%);
}
/* the hero paints its own field; it must not get a second one */
.ds-app .banner-section.ds-blockwrap::before { display: none; }

/* (c) the grain ------------------------------------------------------------ */
.ds-app .ds-blockwrap::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: .025; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.ds-app.dark .ds-blockwrap::after { opacity: .04; }

/* (d) the joins ------------------------------------------------------------ */
.ds-app .ds-blockwrap + .ds-blockwrap { border-top: 1px solid transparent; }
.ds-app .ds-blockwrap + .ds-blockwrap::before,
.ds-app .ds-blockwrap + .ds-blockwrap::after { /* keep the two layers above */ }
.ds-app .ds-blockwrap + .ds-blockwrap {
  border-image: linear-gradient(90deg,
    rgba(var(--p4), 0) 0%, rgba(var(--p4), .16) 22%, rgba(var(--p4), .16) 78%, rgba(var(--p4), 0) 100%) 1;
}

/* (e) lit surfaces --------------------------------------------------------- */
.ds-app .single-item,
.ds-app .contact-card,
.ds-app .contact-form-box,
.ds-app .comparison-table,
.ds-app .counter_right_side,
.ds-app .faq,
.ds-app .pricing_section > div { position: relative; }
.ds-app .single-item::after,
.ds-app .contact-card::after,
.ds-app .contact-form-box::after,
.ds-app .counter_right_side::after,
.ds-app .pricing_section > div::after {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px; pointer-events: none;
  background: linear-gradient(90deg,
    rgba(var(--p8), 0) 0%, rgba(var(--p8), .16) 30%, rgba(var(--p8), .16) 70%, rgba(var(--p8), 0) 100%);
}
.ds-app .single-item::after { top: 1px; border-radius: inherit; }

/* the CTA band is the one place the glow is meant to be felt */
.ds-app .cta-section {
  background-image:
    radial-gradient(120% 140% at 50% 120%, rgba(var(--p1), .22) 0%, rgba(var(--p1), 0) 60%),
    linear-gradient(180deg, rgba(var(--p7), .35) 0%, rgba(var(--p7), .85) 100%);
}
.ds-app .cta-section::before { opacity: .55; }

/* the footer, lifted off the page above it the same way */
.ds-app .footer_section {
  position: relative;
  background-color: rgba(var(--p12), 1);
  background-image:
    radial-gradient(120% 90% at 12% -10%, rgba(var(--p1), .26) 0%, rgba(var(--p1), 0) 58%),
    radial-gradient(90% 70% at 92% 8%, rgba(var(--p2), var(--ds-tint-warm)) 0%, rgba(var(--p2), 0) 60%),
    linear-gradient(180deg, rgba(var(--p12), .90) 0%, rgba(var(--p12), 1) 62%);
  overflow: hidden;
}
/* ⛔ THE GRID IS THE SITE'S OWN MOTIF, NOT A PICTURE OF ONE. The hero draws a
   perspective field of squares; the footer answers it with the same grid seen
   flat, fading out as it falls. Two repeating gradients and a mask — no asset to
   download, no photograph to look dated, and it recolours with the palette. */
.ds-app .footer_section::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    repeating-linear-gradient(90deg, rgba(var(--p6), .05) 0 1px, transparent 1px 88px),
    repeating-linear-gradient(0deg,  rgba(var(--p6), .05) 0 1px, transparent 1px 88px);
  -webkit-mask-image: radial-gradient(120% 80% at 50% 0%, #000 0%, transparent 72%);
          mask-image: radial-gradient(120% 80% at 50% 0%, #000 0%, transparent 72%);
  opacity: .8;
}
.ds-app .footer_section > * { position: relative; z-index: 1; }

/* the counter panel. The template paints it `bg9-color` — pure black — which is a
   dramatic slab on their white page and an invisible one on ours. It takes the
   surface variables instead, so it is a panel on both skins. */
.ds-app .counter_right_side {
  background: linear-gradient(145deg, rgba(var(--p7), .95) 0%, rgba(var(--p7), .62) 100%);
  border: 1px solid rgba(var(--p4), .16);
  overflow: hidden;
}
.ds-app .counter_right_side .cus-border { border-color: rgba(var(--p4), .16) !important; }
.ds-app .counter_right_side .p6-color { color: rgba(var(--p4), 1) !important; }
.ds-app .counter_right_side .display-two { font-size: clamp(2.2rem, 4vw, 3.4rem); line-height: 1.05; }

/* the inner banner: their carousel canvas lives at 2 here too, and the band needs
   a floor so a short page still has a proper masthead */
.ds-app .banner-section.inner-banner { min-height: 420px; display: flex; align-items: flex-end; }
@media (max-width: 991px) { .ds-app .banner-section.inner-banner { min-height: 320px; } }

/* =====================================================================
   -- 15 --  THE SLIDE DIAGRAMS
   One drawing per hero slide, in the column the android robot used to
   occupy. Everything is a CLASS, never a fill/stroke attribute, so the
   palette variables reach it and both skins are the same markup. All the
   motion is CSS keyframes: the browser runs them off the main thread, they
   survive a tab that was loaded in the background, and one media query
   stops the lot for `prefers-reduced-motion`.
   ===================================================================== */
.ds-app .ds-art { width: 100%; height: auto; max-height: 68vh; display: block; overflow: visible;
  filter: drop-shadow(0 18px 44px rgba(0, 0, 0, .45)); }
.ds-app .ds-art__g0 { stop-color: rgba(var(--p1), .9); }
.ds-app .ds-art__g1 { stop-color: rgba(var(--p2), .9); }


/* ⛔ Nothing in a scene is ENCLOSED any more. The drawings used to sit in a product
   window and their contents in tiles and row plates; on a hero with no other straight
   edges, every one of those rectangles read as a screenshot pasted onto the
   composition. Boxes became baselines — a hairline under a field, a hairline between
   rows, a rule under a heading — and the only filled shapes left anywhere are the
   ones the platform itself acted on. */
.ds-app .ds-art__base    { stroke: rgba(var(--p4), .30); stroke-width: 1.4; stroke-linecap: round; }
.ds-app .ds-art__rowline { stroke: rgba(var(--p4), .14); stroke-width: 1; }
.ds-app .ds-art__vsep    { stroke: rgba(var(--p4), .18); stroke-width: 1; }
.ds-app .ds-art__glow    { fill: rgba(var(--p2), .12); filter: blur(7px); }
.ds-app .ds-art__who {
  fill: rgba(var(--p1), .16);
  stroke: rgba(var(--p1), .70); stroke-width: 1.4;
}
.ds-app .ds-art__label { fill: rgba(var(--p8), .55); }
.ds-app .ds-art__label--dim { fill: rgba(var(--p4), .34); }

/* -- BPMN -------------------------------------------------------------- */
.ds-app .ds-art__wire { stroke: rgba(var(--p4), .40); stroke-width: 1.6; }
.ds-app .ds-art__evt  { fill: none; stroke: rgba(var(--p1), .9); stroke-width: 2; }
.ds-app .ds-art__evt--end { stroke: rgba(var(--p2), 1); stroke-width: 3.4; }
.ds-app .ds-art__task {
  fill: rgba(var(--p1), .10); stroke: rgba(var(--p1), .55); stroke-width: 1.6;
}
.ds-app .ds-art__task--muted { fill: rgba(var(--p4), .06); stroke: rgba(var(--p4), .30); }
.ds-app .ds-art__gate  { fill: rgba(var(--p2), .12); stroke: rgba(var(--p2), .85); stroke-width: 1.8; }
.ds-app .ds-art__gatex { stroke: rgba(var(--p2), .85); stroke-width: 1.8; fill: none; }
/* the token, running the happy path: start -> task -> gateway -> approve -> end */
.ds-app .ds-art__token { fill: rgba(var(--p2), 1); filter: drop-shadow(0 0 7px rgba(var(--p2), .85)); }
.ds-app .ds-art__token { animation: ds-token 7s cubic-bezier(.45,0,.55,1) infinite; }
@keyframes ds-token {
  0%   { transform: translate(66px, 240px); opacity: 0; }
  6%   { transform: translate(66px, 240px); opacity: 1; }
  20%  { transform: translate(177px, 240px); }
  34%  { transform: translate(290px, 240px); }
  46%  { transform: translate(332px, 240px); }
  56%  { transform: translate(332px, 164px); }
  68%  { transform: translate(417px, 164px); }
  82%  { transform: translate(498px, 164px); }
  94%  { transform: translate(514px, 240px); opacity: 1; }
  100% { transform: translate(514px, 240px); opacity: 0; }
}
/* the rule that decides the gateway */
.ds-app .ds-art__brain line   { stroke: rgba(var(--p2), .45); stroke-width: 1.3; }
.ds-app .ds-art__brain circle { fill: rgba(var(--p2), .45); }
.ds-app .ds-art__brainout     { fill: rgba(var(--p2), 1) !important; animation: ds-think 2.6s ease-in-out infinite; }
.ds-app .ds-art__brainstem    { stroke-dasharray: 5 7; animation: ds-march 1.6s linear infinite; }
@keyframes ds-think { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }
@keyframes ds-march { to { stroke-dashoffset: -24; } }

/* -- FORMS ------------------------------------------------------------- */
.ds-app .ds-art__typed {
  fill: rgba(var(--p8), .50); transform-origin: left center; transform-box: fill-box;
  animation: ds-type 5.5s cubic-bezier(.3,0,.2,1) infinite;
}
/* the one field the rules filled in — the only green in the scene */
.ds-app .ds-art__typed--auto { fill: rgba(var(--p2), .95); }
@keyframes ds-type {
  0%, 8%   { transform: scaleX(0); opacity: .5; }
  26%, 74% { transform: scaleX(1); opacity: 1; }
  92%,100% { transform: scaleX(0); opacity: .5; }
}
.ds-app .ds-art__caret { stroke: rgba(var(--p4), .7); stroke-width: 2; fill: none; stroke-linecap: round; }
/* ⛔ NOT `url(#dsArtG)`. That id is referenced here and DEFINED NOWHERE — every
   scene builds its own gradient under a unique id, so a fixed name can never
   resolve, and an unresolvable paint server is transparent: the form scene's
   submit pill was a hole with a label floating in it. A solid brand fill is
   honest, reads as a button, and cannot rot. */
.ds-app .ds-art__cta   { fill: rgba(var(--p1), .95); }
.ds-app .ds-art__ctatext { fill: rgba(255, 255, 255, .95); }

/* -- THE CASE BOARD ---------------------------------------------------- */
/* ⛔ `ds-arrive` WAS REFERENCED HERE AND ON `.ds-art__kpi` AND DEFINED NOWHERE.
   An unknown animation-name is not an error in CSS — it is silently ignored — so
   the case rows and the KPI block simply never animated, and the staggered
   `animation-delay` the builders take such care to emit was staggering nothing.
   Second dangling name in this file after `url(#dsArtG)`; both found by reading
   the scenes rather than by anything failing.

   ⛔ OPACITY ONLY. `.ds-art__case` is a `<g transform="translate(...)">` — that is
   how the board stacks its rows — and a CSS transform OVERRIDES the presentation
   attribute, so animating one here would collapse all six rows onto each other. */
@keyframes ds-arrive {
  0%, 5%   { opacity: 0; }
  18%, 92% { opacity: 1; }
  100%     { opacity: 0; }
}
.ds-app .ds-art__case  { animation: ds-arrive 7s ease-out infinite; }
.ds-app .ds-art__pill  { stroke-width: 1; }
.ds-app .ds-art__pill--done { fill: rgba(var(--p2), .18); stroke: rgba(var(--p2), .55); }
.ds-app .ds-art__pill--run  { fill: rgba(var(--p1), .20); stroke: rgba(var(--p1), .60); }
.ds-app .ds-art__pill--wait { fill: rgba(var(--p4), .10); stroke: rgba(var(--p4), .34); }
.ds-app .ds-art__pilltext   { fill: rgba(var(--p8), .60); }
.ds-app .ds-art__track      { fill: rgba(var(--p4), .12); }
/* ⛔ `transform-box: fill-box`, AND WITHOUT IT THE BAR LEAVES ITS TRACK.
   In SVG `transform-origin` resolves against the VIEW BOX by default, so
   `left center` meant x=0 of the 560-wide canvas — not the rect's own left edge.
   Scaling about that point slid every progress bar out of its track and into the
   case-name column, four rows deep. `.ds-art__bar` and `.ds-art__typed` both
   declare `fill-box`; this one never did, and it had been wrong on the home hero
   the whole time — invisible there only because the loop kept it moving. */
.ds-app .ds-art__fillbar {
  fill: rgba(var(--p2), .85);
  transform-origin: left center; transform-box: fill-box;
  animation: ds-advance 5.5s cubic-bezier(.35,0,.2,1) infinite;
}
@keyframes ds-advance {
  0%, 10%  { transform: scaleX(.12); }
  55%, 78% { transform: scaleX(calc(var(--ds-fill, 1) * .7)); }
  92%,100% { transform: scaleX(var(--ds-fill, 1)); }
}

/* -- THE HINT ---------------------------------------------------------- */
.ds-app .ds-art__spark path { fill: rgba(var(--p2), 1); }
.ds-app .ds-art__sparkhalo  { fill: rgba(var(--p2), .12); }
.ds-app .ds-art__spark      { animation: ds-spark 3.2s ease-in-out infinite; transform-box: fill-box; }
@keyframes ds-spark {
  0%, 100% { opacity: .45; }
  45%      { opacity: 1; }
}
.ds-app .ds-art__hint {
  stroke: rgba(var(--p2), .55); stroke-width: 1.4; fill: none;
  stroke-dasharray: 4 6; animation: ds-march 1.8s linear infinite;
}
.ds-app .ds-art__reveal {
  fill: rgba(var(--p2), .14); filter: blur(8px);
  animation: ds-reveal 5.5s ease-in-out infinite;
}
@keyframes ds-reveal {
  0%, 22%  { opacity: 0; }
  38%, 86% { opacity: 1; }
  100%     { opacity: 0; }
}

/* -- THE REPORT -------------------------------------------------------- */
.ds-app .ds-art__kpi   { animation: ds-arrive 7s ease-out infinite; }
.ds-app .ds-art__kpinum { fill: rgba(var(--p8), .72); }
.ds-app .ds-art__kpinum--hero { fill: rgba(var(--p2), .95); }
.ds-app .ds-art__trendup { stroke: rgba(var(--p2), .85); stroke-width: 1.6; fill: none; stroke-linecap: round; }
.ds-app .ds-art__grid  { stroke: rgba(var(--p4), .12); stroke-width: 1; }
.ds-app .ds-art__bar {
  fill: rgba(var(--p1), .62);
  transform-origin: center bottom; transform-box: fill-box;
  animation: ds-grow 5.5s cubic-bezier(.3,0,.2,1) infinite;
}
.ds-app .ds-art__bar--hot { fill: rgba(var(--p2), .9); }
@keyframes ds-grow {
  0%, 6%   { transform: scaleY(.06); opacity: .5; }
  34%, 80% { transform: scaleY(1); opacity: 1; }
  96%,100% { transform: scaleY(.06); opacity: .5; }
}
.ds-app .ds-art__trend {
  stroke: rgba(var(--p2), .95); stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 520; animation: ds-draw 5.5s cubic-bezier(.3,0,.2,1) infinite;
  filter: drop-shadow(0 0 5px rgba(var(--p2), .45));
}
@keyframes ds-draw {
  0%, 6%   { stroke-dashoffset: 520; }
  46%, 84% { stroke-dashoffset: 0; }
  100%     { stroke-dashoffset: 520; }
}
.ds-app .ds-art__trenddot { fill: rgba(var(--p2), 1); animation: ds-pop 5.5s ease-out infinite; }
@keyframes ds-pop { 0%, 10% { opacity: 0; } 24%, 86% { opacity: 1; } 100% { opacity: 0; } }

/* -- THE UNTANGLE ------------------------------------------------------
   The BEFORE is drawn in INK, never in brand colour: it is the process as the
   customer brings it, and painting it blue would claim we drew it. The AFTER is
   the only thing on the canvas that carries the palette, which is the whole
   argument of the picture in one decision about colour. */
.ds-app .ds-art__knot    { stroke: rgba(var(--p4), .38); stroke-width: 1.9; stroke-linecap: round; }
.ds-app .ds-art__knotdot { fill: rgba(var(--p4), .42); }
.ds-app .ds-art__knotend { stroke: rgba(var(--p4), .62); stroke-width: 2.4; fill: none; stroke-linecap: round; }
.ds-app.dark .ds-art__knot    { stroke: rgba(var(--p6), .30); }
.ds-app.dark .ds-art__knotdot { fill: rgba(var(--p6), .34); }
.ds-app.dark .ds-art__knotend { stroke: rgba(var(--p6), .50); }

.ds-app .ds-art__route {
  stroke: rgba(var(--p1), .95); stroke-width: 3.6; stroke-linecap: round; stroke-linejoin: round;
  /* the AFTER sits ON the mess, so it needs to be lifted off it */
  filter: drop-shadow(0 2px 6px rgba(var(--p5), .9));
}
/* the travelling highlight: one short dash chasing the case along the route, so
   the eye is led left to right the way the sentence beside it reads. */
.ds-app .ds-art__routelit {
  stroke: rgba(var(--p2), .95); stroke-width: 3.6; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 90 520; filter: drop-shadow(0 0 6px rgba(var(--p2), .5));
  animation: ds-untangle-lit 6.5s cubic-bezier(.45,0,.55,1) infinite;
}
@keyframes ds-untangle-lit { from { stroke-dashoffset: 110; } to { stroke-dashoffset: -500; } }

.ds-app .ds-art__stop {
  fill: rgba(var(--p5), 1); stroke: rgba(var(--p1), .95); stroke-width: 2.8;
  filter: drop-shadow(0 1px 4px rgba(var(--p5), .95));
}
.ds-app .ds-art__stop--end { stroke: rgba(var(--p2), 1); stroke-width: 3.2; }
.ds-app .ds-art__tick { stroke: rgba(var(--p2), 1); stroke-width: 2.6; fill: none;
                        stroke-linecap: round; stroke-linejoin: round; }
.ds-app .ds-art__runner {
  fill: rgba(var(--p2), 1); filter: drop-shadow(0 0 8px rgba(var(--p2), .85));
  animation: ds-untangle-run 6.5s cubic-bezier(.45,0,.55,1) infinite;
}
@keyframes ds-untangle-run {
  0%       { transform: translate(64px, 262px);  opacity: 0; }
  6%       { transform: translate(64px, 262px);  opacity: 1; }
  28%      { transform: translate(178px, 228px); }
  50%      { transform: translate(292px, 208px); }
  72%      { transform: translate(406px, 176px); }
  92%,100% { transform: translate(510px, 142px); opacity: 1; }
}

/* this band's drawing is never frozen (see artUntangle) and never boxed: at
   xl it fills the half column the template reserved for an illustration. */
.ds-app .dicscuss_projects .ds-cta-art { position: relative; z-index: 1; }
.ds-app .dicscuss_projects .ds-cta-art .ds-art { max-height: 460px; margin-inline: auto; }
@media (prefers-reduced-motion: reduce) {
  .ds-app .ds-art__runner { transform: translate(510px, 142px); }
  .ds-app .ds-art__routelit { stroke-dashoffset: -500; }
}

/* -- THE SAME SCENES, INSIDE A PAGE HEADER ------------------------------
   ⛔ EVERY LOOP ABOVE ENDS BY COLLAPSING TO NOTHING, and on the home hero that is
   right: the drawing is the subject, you watch it, it plays. In a header it is
   wrong, and the screenshot that proved it caught /products/erp with its chart
   flat — bars at scaleY(.06), the trend line a green stub. Every 5.5s the header
   empties itself in the corner of the eye of somebody trying to read a headline.

   So in the banner the reveal runs ONCE and holds the finished picture. What
   still loops is only the small living detail — the BPMN token on its route, the
   spark on the flagged bar, the marching hint — which is what makes the drawing
   look live instead of printed. One-shot for the structure, a loop for the pulse.

   The per-element `animation-delay` the builders emit is inherited unchanged, so
   the bars still come up in sequence; `both` holds the first frame during that
   delay instead of flashing the finished state first. */
@keyframes ds-bnr-grow { from { transform: scaleY(.06); opacity: .5; } to { transform: scaleY(1); opacity: 1; } }
@keyframes ds-bnr-draw { from { stroke-dashoffset: 520; }              to { stroke-dashoffset: 0; } }
@keyframes ds-bnr-type { from { transform: scaleX(0);   opacity: .5; } to { transform: scaleX(1); opacity: 1; } }
@keyframes ds-bnr-adv  { from { transform: scaleX(.12); }              to { transform: scaleX(var(--ds-fill, 1)); } }
@keyframes ds-bnr-fade { from { opacity: 0; }                          to { opacity: 1; } }

.ds-app .ds-bnr-art .ds-art__bar      { animation: ds-bnr-grow 1.05s cubic-bezier(.3,0,.2,1) both; }
.ds-app .ds-bnr-art .ds-art__trend    { animation: ds-bnr-draw 1.60s cubic-bezier(.3,0,.2,1) both; }
.ds-app .ds-bnr-art .ds-art__trenddot { animation: ds-bnr-fade  .90s ease-out .9s both; }
.ds-app .ds-bnr-art .ds-art__typed    { animation: ds-bnr-type 1.00s cubic-bezier(.3,0,.2,1) both; }
.ds-app .ds-bnr-art .ds-art__fillbar  { animation: ds-bnr-adv  1.40s cubic-bezier(.35,0,.2,1) both; }
.ds-app .ds-bnr-art .ds-art__reveal,
.ds-app .ds-bnr-art .ds-art__case,
.ds-app .ds-bnr-art .ds-art__kpi      { animation: ds-bnr-fade  .80s ease-out both; }

@media (prefers-reduced-motion: reduce) {
  .ds-app .ds-art * { animation: none !important; }
  .ds-app .ds-art__typed { transform: scaleX(1); }
  .ds-app .ds-art__token { transform: translate(417px, 164px); }
}
/* below lg the slide stacks and the drawing would push the copy off the fold */
/* the mobile hide for .slide-img now lives at the END of this file — see §20 */

/* =====================================================================
   -- 16 --  THE BUTTONS
   The template ships four unrelated button designs and none of them is
   finished:

     `.btn`          a flat blue pill whose only hover is the two-layer label
                     slide. Flat fill, no lift, no shadow, no pressed state,
                     and — the one that matters — NO FOCUS RING, so the site
                     cannot be used from a keyboard.
     `.btn.second`   `background: transparent; border: 1px solid; color: black`.
                     A naked outline with a hardcoded black label: invisible
                     text on the dark skin unless every caller remembers to add
                     `p8-color` by hand, which is exactly the kind of rule that
                     gets forgotten once and ships.
     `.btn2`         a fixed `width: 190px`. A Russian or Armenian label does
                     not fit in 190px, so the header CTA clips in two of the
                     three locales the project ships.
     `.price_btn`    styled by nothing at all — `bg15-color` and a pill radius.

   ⛔ The label slide is THEIRS and stays: `.btn-text-one` rides up as
   `.btn-text-two` rides in, and it is the site's signature. Everything around
   it is rebuilt as ONE system:

     · one height (52px), one radius, one weight, one letter-spacing
     · the primary is a gradient with an inner top highlight and a brand-tinted
       shadow, so it reads as lit rather than as painted
     · the secondary is glass — a translucent surface with a hairline — not an
       outline, and it takes its colour from the palette instead of `black`
     · lift on hover, press on :active, and a real :focus-visible ring
     · intrinsic width with a sensible minimum, so all three locales fit
   ===================================================================== */
.ds-app .btn,
.ds-app .btn2,
.ds-app .price_btn {
  --ds-btn-h: 52px;
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: auto; min-width: 172px; height: var(--ds-btn-h);
  padding: 0 30px;
  border-radius: 999px;
  font-size: 15px; font-weight: 600; letter-spacing: .01em; line-height: 1;
  text-decoration: none; cursor: pointer; overflow: hidden;
  transition: transform .28s cubic-bezier(.2,.8,.2,1), box-shadow .28s, border-color .28s, background .28s;
}
.ds-app .btn span,
.ds-app .btn2 span { font-size: 15px; font-weight: 600; }

/* --- primary ---------------------------------------------------------- */
.ds-app .btn,
.ds-app .btn2,
.ds-app .price_btn {
  background: linear-gradient(180deg, rgba(var(--p1), 1) 0%, rgba(var(--p1), .84) 100%) !important;
  border: 1px solid rgba(var(--p1), 1) !important;
  color: #fff !important;
  box-shadow: 0 10px 24px -10px rgba(var(--p1), .75), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.ds-app .btn:hover,
.ds-app .btn2:hover,
.ds-app .price_btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 34px -12px rgba(var(--p1), .9), inset 0 1px 0 rgba(255, 255, 255, .3);
}
.ds-app .btn:active,
.ds-app .btn2:active,
.ds-app .price_btn:active { transform: translateY(0); box-shadow: 0 6px 14px -8px rgba(var(--p1), .7); }

/* --- secondary: glass, not a naked outline ---------------------------- */
.ds-app .btn.second {
  background: rgba(var(--p8), .07) !important;
  border: 1px solid rgba(var(--p8), .22) !important;
  color: rgba(var(--p8), 1) !important;
  backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 0 rgba(var(--p8), .10);
}
.ds-app .btn.second:hover {
  background: rgba(var(--p8), .12) !important;
  border-color: rgba(var(--p1), .75) !important;
  box-shadow: 0 12px 26px -14px rgba(0, 0, 0, .55);
}
/* the label rides in the same colour as the button it is in — the template
   hardcoded `color: black` on the secondary and left the spans to fend for
   themselves */
.ds-app .btn.second .btn-text-one,
.ds-app .btn.second .btn-text-two { color: rgba(var(--p8), 1) !important; }

/* --- the keyboard, which the template forgot entirely ----------------- */
.ds-app .btn:focus-visible,
.ds-app .btn2:focus-visible,
.ds-app .price_btn:focus-visible,
.ds-app .read-more:focus-visible,
.ds-app .slide-button:focus-visible,
.ds-app .ds-rail__icon:focus-visible,
.ds-app .header-section .dark_btn:focus-visible,
.ds-app .ds-lang-btn:focus-visible,
.ds-app .navbar-nav a:focus-visible,
.ds-app .navbar-nav button:focus-visible {
  outline: 2px solid rgba(var(--p2), 1);
  outline-offset: 3px;
}

/* --- the carousel arrows, to the same system -------------------------- */
.ds-app .banner-section.index-one .slide-button {
  width: 46px; height: 46px;
  background: rgba(var(--p8), .06) !important;
  border: 1px solid rgba(var(--p8), .22) !important;
  transition: background .28s, border-color .28s, transform .28s;
}
.ds-app .banner-section.index-one .slide-button:hover {
  background: rgba(var(--p1), 1) !important;
  border-color: rgba(var(--p1), 1) !important;
  transform: translateY(-2px);
}
.ds-app .banner-section.index-one .slide-button i { color: rgba(var(--p8), 1); transition: color .28s; }
.ds-app .banner-section.index-one .slide-button:hover i { color: #fff; }

/* --- the pricing button is full width in its card, so it opts out of the
       minimum and keeps the rest ------------------------------------------ */
.ds-app .price_btn { width: 100%; min-width: 0; }

/* --- the header CTA is the one that must never clip ------------------- */
.ds-app .btn2 { min-width: 0; padding: 0 26px; height: 46px; }
.ds-app .btn2 span { font-size: 14px; }

/* the two label layers need a box to slide inside now that height is ours */
.ds-app .btn .btn-text-one,
.ds-app .btn .btn-text-two,
.ds-app .btn2 .btn-text-one,
.ds-app .btn2 .btn-text-two { white-space: nowrap; }

/* their `.btn-area` put the two buttons flush against each other */
.ds-app .btn-area { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }

/* -- 17 ------------------------------------------------------------------
   THREE FIXES THE BUTTON REBUILD AND THE DIAGRAMS NEEDED.

   (a) The label slide, done with intrinsic width.
       Their `.btn-text-one` / `.btn-text-two` are BOTH `position: absolute;
       width: 100%`, so nothing is in flow and the button has no width of its
       own — which is why the template had to hardcode `width: 190px` on `.btn2`,
       and why that clips the Russian and Armenian labels. Stack the two labels in
       one grid cell instead: both stay in flow, the button sizes to the longer of
       them in whatever language it is, and the slide becomes a transform, which
       is cheaper than animating `top` anyway. */
/* ⛔ AND NOT `!important` ON `display`. It was written that way out of habit and
   it cost the phone header: `.ds-app .btn2` and `.ds-app .d-none` have the SAME
   specificity, so an `!important` here — stated later in the file — beat
   Bootstrap's own `display: none !important` and every other display utility.
   The header's desktop CTA is marked `d-none d-xl-block`, so it stopped hiding
   below 1200px: measured in a 390px iframe as a 165px pill sitting on top of the
   logo. `display` belongs to the utilities; a component rule states it plainly
   and loses to them, which is the whole point of a utility. Nothing of theirs
   sets it with `!important` — checked — so plain wins here anyway. */
.ds-app .btn,
.ds-app .btn2,
.ds-app .price_btn { display: inline-grid; grid-auto-flow: row; align-items: center; justify-items: center; }
.ds-app .btn .btn-text-one, .ds-app .btn .btn-text-two,
.ds-app .btn2 .btn-text-one, .ds-app .btn2 .btn-text-two {
  position: static !important; top: auto !important; left: auto !important;
  width: auto !important; grid-column: 1; grid-row: 1;
  transform: none;
  transition: transform .42s cubic-bezier(.2, .8, .2, 1), opacity .3s ease;
}
.ds-app .btn .btn-text-two,
.ds-app .btn2 .btn-text-two { transform: translateY(150%); opacity: 0; }
.ds-app .btn:hover .btn-text-one,
.ds-app .btn2:hover .btn-text-one { transform: translateY(-150%); opacity: 0; }
.ds-app .btn:hover .btn-text-two,
.ds-app .btn2:hover .btn-text-two { transform: translateY(0); opacity: 1; }

/*  (b) The header CTA must never shrink. It lives in a flex row with the theme
        toggle, the drawer button and the language switcher, and with an intrinsic
        width it was the first thing the row squeezed — down to 54px, with the
        label wrapped onto two lines. */
.ds-app .header-section .btn2 { flex: 0 0 auto; white-space: nowrap; }
.ds-app .btn,
.ds-app .btn2,
.ds-app .price_btn { white-space: nowrap; }

/*  (c) `.slide-img` is an inline span in their markup, so it shrink-wrapped to
        300px inside a 558px column and the diagram rendered at half size. */
/*  ⛔ The banner row sits in a `container-fluid px-0`, so the column holding the
        drawing runs straight off the right edge of the viewport and the panel was
        cut in half. It also has to clear the chip strip that crosses the bottom of
        the hero. Cap it, inset it, and centre it in what is left. */
.ds-app .banner-section .slide-img {
  display: flex; align-items: center; justify-content: center;
  width: 100%;
  padding-right: clamp(20px, 4.5vw, 96px);
}
/* ⛔ The float runs on the ACTIVE slide only, and starts after the entrance.
   The template slides this column in from `translateX(150%)` as a slide becomes
   active; an unconditional float animation sets `transform` on every slide and
   silently overrides that, so the diagrams stopped entering and just appeared. */
.ds-app .banner-translate-carousel .swiper-slide-active .slide-img {
  animation: ds-art-float 9s ease-in-out 1.6s infinite;
}
.ds-app .banner-section .slide-img .ds-art {
  width: 100%; max-width: 520px;
  max-height: min(56vh, 430px);
}
@media (max-width: 1399px) {
  .ds-app .banner-section .slide-img .ds-art { max-width: 430px; max-height: min(48vh, 360px); }
}
@keyframes ds-art-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}
@media (prefers-reduced-motion: reduce) { .ds-app .banner-section .slide-img { animation: none; } }

/* -- 18 ------------------------------------------------------------------
   THE CLOSING BAND.

   `.dicscuss_projects` is painted by `.parallax`, whose only rule is
   `background: url("images/counter.png")` — a light stock photograph. On the
   template's white page that is a texture; on this one it is a bright slab
   across the bottom of every page. Worse, it made the band depend on an image
   that the asset step had already dropped as unused (it WAS unused, until this
   section started using `.parallax`), so a fresh import would have shown no
   background at all while the stand still showed the old one — the two would
   have disagreed forever.

   The band paints itself from the palette instead: a brand bloom rising out of
   the bottom edge over a deep surface, which is what the closing call wants and
   needs no asset to exist. */
.ds-app .dicscuss_projects.parallax {
  background-image:
    radial-gradient(110% 150% at 50% 130%, rgba(var(--p1), .34) 0%, rgba(var(--p1), 0) 62%),
    linear-gradient(180deg, rgba(var(--p7), .30) 0%, rgba(var(--p12), .92) 100%) !important;
  background-color: rgba(var(--p5), 1) !important;
  background-attachment: scroll !important;
  background-size: cover, cover !important;
  position: relative;
}
/* their copy in this band is written for a dark ground in both skins */
.ds-app .dicscuss-content .p6-color { color: rgba(var(--p8), 1) !important; }

/* =====================================================================
   -- 19 --  THE CONSOLE'S CHROME BLEEDS INWARD
   The whole stylesheet is scoped to `.ds-app` so the site cannot repaint
   the builder's console. That is only half the boundary, and the half
   that was missing cost the light skin its header.

   The platform serves SIXTEEN stylesheets of its own before ours —
   base.css, theme-common.css, dark.css, no-admin-site-dev.css and the
   rest — and they are NOT scoped. They style bare Bootstrap class names,
   and this site is built on Bootstrap, so wherever the two vocabularies
   overlap the console paints our markup:

     `.navbar`        a hardcoded linear-gradient(135deg, rgb(13,22,36) …)
                      — the dark bar that stayed dark behind a white page
                      in the standard skin, taking the nav labels and the
                      three round buttons down with it
     `.dropdown-menu` a flat rgb(25,52,80) that beat our own gradient

   Neither is a colour from any palette here. `!important` is used
   deliberately and only here: the console's rules arrive with weight we
   cannot outrank by specificity alone, and this is a boundary, not a
   style choice. Anything the site genuinely wants on these elements is
   painted by the rules above, which now have something to paint on.
   ===================================================================== */
.ds-app .navbar {
  background: none !important;
  background-color: transparent !important;
  padding: 0 !important;
  border: 0 !important;
  box-shadow: none !important;
  color: inherit !important;
}
.ds-app .header-section .navbar .navbar-nav .dropdown-menu,
.ds-app .header-section .navbar .navbar-nav .drop_menu {
  background-image: linear-gradient(180deg, rgba(var(--p5), .97) 0%, rgba(var(--p5), .94) 100%) !important;
  background-color: rgba(var(--p5), .97) !important;
  border: 1px solid rgba(var(--p4), .16) !important;
  box-shadow: 0 14px 44px rgba(0, 0, 0, .18) !important;
}
.ds-app.dark .header-section .navbar .navbar-nav .dropdown-menu,
.ds-app.dark .header-section .navbar .navbar-nav .drop_menu {
  background-image: linear-gradient(180deg, rgba(var(--p12), .97) 0%, rgba(var(--p7), .95) 100%) !important;
  background-color: rgba(var(--p12), .97) !important;
  border-color: rgba(var(--p4), .18) !important;
}
.ds-app .header-section .navbar .navbar-nav .dropdown-item {
  color: rgba(var(--p8), .92) !important;
}
.ds-app .header-section .navbar .navbar-nav .dropdown-item:hover,
.ds-app .header-section .navbar .navbar-nav .dropdown-item.active {
  color: rgba(var(--p1), 1) !important;
  background: rgba(var(--p1), .10) !important;
}
/* the bar the header actually wants: none at rest, the palette's own when stuck */
.ds-app .header-section.header-fixed {
  background-color: rgba(var(--p5), .92) !important;
  backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(var(--p4), .14);
  box-shadow: 0 6px 24px rgba(0, 0, 0, .10) !important;
}
.ds-app.dark .header-section.header-fixed {
  background-color: rgba(var(--p12), .90) !important;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .34) !important;
}


/* -- 20 ------------------------------------------------------------------
   The h1 that carries the home page's headline. It exists for a reader and a
   crawler, not for the eye: the two spans inside it keep every visual property
   and their own slide-in, so this wrapper must add nothing at all. */
.ds-app .ds-hero-h1 { margin: 0; padding: 0; font: inherit; color: inherit; }

/* -- 21 ------------------------------------------------------------------
   Three things the footer got wrong, all of them mine.

   (a) THE NEWSLETTER FIELD. It went through two wrong states. First §5 above
       paints every `.single-input input` dark, for the contact form on the dark
       skin — and that rule reached in here too, putting a near-black field
       inside their white pill: two surfaces fighting inside one control. The
       two-line patch that followed only neutralised §5 and left their pill
       alone, which is where it stayed looking cheap:

         · the pill is `--p6`, and `--p6` is 255,255,255 in BOTH themes, so a
           pure-white slab sat on a dark navy footer, the loudest thing below
           the fold;
         · the button had no width of its own — `px-4 py-2` around a ~28px icon
           — so a blue blob decided the whole control's height and overhung the
           field it belongs to;
         · the text was `fs-ten`, tiny next to that blob, and the placeholder had
           no colour of its own, so the field read empty even when it was not.

       It is now a field cut INTO the footer rather than stuck onto it: one
       stated height that both children obey, a translucent ground off the
       footer's own white, a hairline that brightens on hover and takes the
       brand ring on focus, and a circular button sized from that height with the
       icon as a glyph rather than a slab. `--ds-nl-h` is the single number; the
       mobile step changes it once and everything follows.

   (b) THE LIST BULLET WAS AN ORANGE PNG. `images/point.png` is the template's
       own accent dot, and it survived the repaint because it is an image, not a
       colour — the only orange left on the site, in the footer's link column. A
       dot is two lines of CSS and follows the palette.

   (c) The social circles now sit under a heading like their neighbours; they
       only need to stop being centred inside a column that is left-aligned. */
.ds-app .footer_section .newsletter .single-input {
  --ds-nl-h: 56px;
  height: var(--ds-nl-h);
  padding: 6px 6px 6px 22px;
  gap: 10px;
  background: rgba(var(--p6), .07);
  border: 1px solid rgba(var(--p6), .16);
  transition: background-color .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.ds-app .footer_section .newsletter .single-input:hover {
  background: rgba(var(--p6), .10);
  border-color: rgba(var(--p6), .26);
}
.ds-app .footer_section .newsletter .single-input:focus-within {
  background: rgba(var(--p6), .11);
  border-color: rgba(var(--p1), .95);
  box-shadow: 0 0 0 4px rgba(var(--p1), .20);
}
.ds-app .footer_section .newsletter .single-input input {
  flex: 1 1 auto;
  min-width: 0;                       /* or a long address stretches the pill */
  height: 100%;
  padding: 0;
  font-size: 15px;
  line-height: 1;
  color: rgba(var(--p6), .96);
  background: transparent;
  border: 0;
  box-shadow: none;
}
.ds-app .footer_section .newsletter .single-input input:focus {
  background: transparent;
  box-shadow: none;
  color: rgba(var(--p6), 1);
}
.ds-app .footer_section .newsletter .single-input input::placeholder {
  color: rgba(var(--p6), .45);
  opacity: 1;                         /* Firefox dims placeholders on its own */
}
.ds-app .footer_section .newsletter .single-input button {
  flex: 0 0 auto;
  width: calc(var(--ds-nl-h) - 12px);
  height: calc(var(--ds-nl-h) - 12px);
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: rgba(var(--p1), 1);
  color: #fff;
  transition: background-color .2s ease, transform .12s ease;
}
.ds-app .footer_section .newsletter .single-input button:hover {
  background: rgba(var(--p3), 1);
}
.ds-app .footer_section .newsletter .single-input button:active { transform: scale(.94); }
.ds-app .footer_section .newsletter .single-input button i {
  font-size: 18px;
  line-height: 1;
}
@media (max-width: 575px) {
  .ds-app .footer_section .newsletter .single-input {
    --ds-nl-h: 50px;
    padding-left: 18px;
  }
  .ds-app .footer_section .newsletter .single-input input { font-size: 14px; }
}
.ds-app .footer_section .ds-bullet {
  flex: 0 0 auto;
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(var(--p2), 1);
  opacity: .85;
}
.ds-app .footer_section .social_info .contact_icon { flex: 0 0 auto; }

/* -- 22 ------------------------------------------------------------------
   THE SPACING SCALE. Their stylesheet offers `pt-120` and a 4px `pt-0…pt-20`
   ladder, which is two vocabularies and neither of them says what a step MEANS.
   Four named steps do, and every section now asks for one of them by role
   (00-boot.js `measure()`): sm closes a seam inside one movement, md follows the
   hero or carries a strip, lg is a new movement, xl is the closing ask.

   The steps shrink on a phone, where 168px of nothing is a scroll, not a pause. */
.ds-app .ds-pt-sm { padding-top: 64px; }    .ds-app .ds-pb-sm { padding-bottom: 64px; }
.ds-app .ds-pt-md { padding-top: 96px; }    .ds-app .ds-pb-md { padding-bottom: 96px; }
.ds-app .ds-pt-lg { padding-top: 128px; }   .ds-app .ds-pb-lg { padding-bottom: 128px; }
.ds-app .ds-pt-xl { padding-top: 176px; }   .ds-app .ds-pb-xl { padding-bottom: 176px; }
@media (max-width: 991px) {
  .ds-app .ds-pt-sm { padding-top: 48px; }  .ds-app .ds-pb-sm { padding-bottom: 48px; }
  .ds-app .ds-pt-md { padding-top: 64px; }  .ds-app .ds-pb-md { padding-bottom: 64px; }
  .ds-app .ds-pt-lg { padding-top: 80px; }  .ds-app .ds-pb-lg { padding-bottom: 80px; }
  .ds-app .ds-pt-xl { padding-top: 96px; }  .ds-app .ds-pb-xl { padding-bottom: 96px; }
}

/* -- 23 ------------------------------------------------------------------
   ⛔ THE LIGHT SKIN HAS TO EARN ITS DEPTH. On the dark skin a card separates
   itself for free: it is lighter than the ground and the eye reads luminance as
   elevation. On light there is nowhere to go — a white card on a near-white page
   has only its hairline, which is why the light skin arrived flat while the dark
   one looked designed. Light gets what light UI always needs: a real surface
   under the card and a shadow that says how far above the page it sits.

   Two shadows, not one: a tight one for the edge (so the card has a defined
   bottom) and a wide soft one for the lift. Both are the ink colour at low alpha,
   never black — black over a blue-grey ground goes muddy.

   None of this is applied on the dark skin, where it would only make mud. */
.ds-app:not(.dark) .single-item,
.ds-app:not(.dark) .contact-form-box,
.ds-app:not(.dark) .counter_right_side,
.ds-app:not(.dark) .pricing-section .single-box,
.ds-app:not(.dark) .ds-panel {
  background: rgba(var(--p6), 1);
  border-color: rgba(var(--p8), .07) !important;
  box-shadow: 0 1px 2px rgba(var(--p8), .05), 0 10px 28px -12px rgba(var(--p8), .12);
  transition: box-shadow .3s ease, transform .3s ease, border-color .3s ease;
}
.ds-app:not(.dark) .single-item:hover,
.ds-app:not(.dark) .ds-panel:hover {
  box-shadow: 0 2px 4px rgba(var(--p8), .06), 0 18px 40px -14px rgba(var(--p8), .18);
  border-color: rgba(var(--p1), .22) !important;
}
/* the tinted band is a real band now, so the line where it starts can be softer */
.ds-app:not(.dark) .bg7-color { border-block: 1px solid rgba(var(--p8), .05); }
/* their ghost numeral was set for a darker ground; on the lifted card it vanished */
.ds-app:not(.dark) .number-area { color: rgba(var(--p8), .07); }

/* -- 24 ------------------------------------------------------------------
   THE SECTOR LIST. Industries used to be the product card again, a few hundred
   pixels below the product cards. A sector is a heading you fall under, not a
   thing you buy, so it is set as a ruled list: the rule is the unit, the icon
   sits in line with the name, and four run across a row. */
.ds-app .ds-sectors .ds-sector {
  padding: 22px 24px 22px 0;
  border-top: 1px solid rgba(var(--p8), .10);
}
.ds-app.dark .ds-sectors .ds-sector { border-top-color: rgba(var(--p4), .22); }
.ds-app .ds-sectors .ds-sector__icon {
  width: 44px; height: 44px; border-radius: 12px;
  background: rgba(var(--p2), .10);
  transition: background .25s ease, transform .25s ease;
}
.ds-app .ds-sectors .ds-sector__link { text-decoration: none; }
.ds-app .ds-sectors .ds-sector__link:hover .ds-sector__icon {
  background: rgba(var(--p2), .18); transform: translateY(-2px);
}
.ds-app .ds-sectors .ds-sector__link:hover .ds-sector__name { color: rgba(var(--p1), 1); }
.ds-app .ds-sectors .ds-sector__name { transition: color .25s ease; }
/* the lead of a wide head should not out-measure the column it sits in */
.ds-app .ds-headwide p { max-width: 46ch; }

/* -- 25 ------------------------------------------------------------------
   THE FIRST PAINT. `main.html` is the only markup the platform renders on the
   server; these rules give it the hero's proportions so the half-second before
   the bridge answers looks like the page loading rather than like a page that
   is not there. Every box is a `.ds-skel`, so it already pulses (§2).

   The heights are the hero's real ones, which is the whole point: when the words
   land nothing jumps, because the space was already the right size. */
.ds-app .ds-boot { min-height: 640px; }
.ds-app .ds-boot__rail {
  position: absolute; inset: 0 auto 0 0; width: 240px;
  background: linear-gradient(180deg, rgba(var(--p7), .26) 0%, rgba(var(--p7), .04) 100%);
  border-right: 1px solid rgba(var(--p4), .12);
}
.ds-app.dark .ds-boot__rail {
  background: linear-gradient(180deg, rgba(var(--p7), .40) 0%, rgba(var(--p7), .10) 100%);
}
.ds-app .ds-boot__copy {
  position: relative; z-index: 2; max-width: 620px;
  padding: 80px 24px 0 clamp(24px, 4vw, 96px);
  display: flex; flex-direction: column; gap: 14px;
}
.ds-app .ds-boot__eyebrow { height: 22px; width: 260px; border-radius: 6px; }
.ds-app .ds-boot__h1      { height: 84px; width: 440px; border-radius: 10px; }
.ds-app .ds-boot__h2      { height: 54px; width: 300px; border-radius: 10px; margin-left: 12px; }
.ds-app .ds-boot__lead    { height: 15px; width: 100%;  border-radius: 5px; margin-top: 8px; }
.ds-app .ds-boot__lead--short { width: 72%; margin-top: 0; }
.ds-app .ds-boot__btns    { display: flex; gap: 14px; margin-top: 16px; }
.ds-app .ds-boot__btn     { height: 46px; width: 156px; border-radius: 999px; }
.ds-app .ds-boot__btn--ghost { width: 148px; opacity: .55; }
.ds-app .ds-boot__art {
  position: absolute; right: 0; top: 150px; width: 44%; height: 300px;
  border-radius: 18px 0 0 18px; opacity: .6;
}
@media (max-width: 991px) {
  .ds-app .ds-boot { min-height: 480px; }
  .ds-app .ds-boot__rail, .ds-app .ds-boot__art { display: none; }
  .ds-app .ds-boot__copy { padding: 56px 20px 0; }
  .ds-app .ds-boot__h1 { height: 56px; width: 82%; }
  .ds-app .ds-boot__h2 { height: 38px; width: 60%; }
}

/* -- 26 ------------------------------------------------------------------
   THE HEADER'S RIGHT CLUSTER. Language, theme, apps: three controls that do
   three unrelated jobs and therefore have to look like one family, or the corner
   reads as three widgets bolted together — which is what it was, a filled blue
   capsule with a caret beside two round ghost icons beside a solid pill.

   All three are now the same 44px round ghost button. The language one carries
   two letters where the others carry a glyph; nothing else differs. */
.ds-app .ds-lang-btn {
  width: 44px; height: 44px; border-radius: 50%;
  background: transparent;
  border: 1px solid rgba(var(--p8), .13);
  padding: 0; cursor: pointer;
  transition: background .22s ease, border-color .22s ease;
}
.ds-app .ds-lang-btn > span {
  font-size: 12px; font-weight: 700; letter-spacing: .04em; line-height: 1;
}
.ds-app .ds-lang-btn:hover { background: rgba(var(--p8), .05); border-color: rgba(var(--p1), .34); }
.ds-app .ds-lang-wrap.is-open .ds-lang-btn {
  background: rgba(var(--p1), .10); border-color: rgba(var(--p1), .55);
}
.ds-app.dark .ds-lang-btn { border-color: rgba(var(--p4), .26); }
.ds-app.dark .ds-lang-btn:hover { background: rgba(var(--p4), .10); }

/* the menu: a card, not a strip of rows with an icon on every line */
.ds-app .ds-lang-wrap .ds-lang-menu {
  position: absolute; top: calc(100% + 12px); right: 0; min-width: 210px; z-index: 40;
  padding: 6px; border-radius: 14px; list-style: none; margin: 0;
  background: rgba(var(--p6), 1);
  border: 1px solid rgba(var(--p8), .09);
  box-shadow: 0 1px 2px rgba(var(--p8), .06), 0 18px 44px -16px rgba(var(--p8), .28);
  opacity: 0; visibility: hidden; transform: translateY(-6px) scale(.98);
  transform-origin: top right;
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
}
.ds-app.dark .ds-lang-wrap .ds-lang-menu {
  background: rgba(var(--p7), 1); border-color: rgba(var(--p4), .20);
}
.ds-app .ds-lang-wrap.is-open .ds-lang-menu { opacity: 1; visibility: visible; transform: none; }
.ds-app .ds-lang-wrap .ds-lang-menu button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 9px;
  background: none; border: 0; text-align: left; cursor: pointer;
  color: rgba(var(--p8), 1);
  transition: background .16s ease;
}
.ds-app .ds-lang-wrap .ds-lang-menu button:hover { background: rgba(var(--p8), .055); }
.ds-app.dark .ds-lang-wrap .ds-lang-menu button:hover { background: rgba(var(--p4), .12); }
.ds-app .ds-lang-menu__name { font-size: 14px; font-weight: 500; }
.ds-app .ds-lang-menu__code {
  margin-left: auto; font-size: 11px; font-weight: 700; letter-spacing: .05em;
  color: rgba(var(--p4), 1);
}
.ds-app .ds-lang-menu__tick { font-size: 15px; color: rgba(var(--p2), 1); margin-left: 2px; }
.ds-app .ds-lang-menu button[aria-current="true"] .ds-lang-menu__code { display: none; }

/* -- 27 ------------------------------------------------------------------
   THE SPLIT SECTION.

   (a) ⛔ THE PICTURE IGNORED ITS COLUMN. Their stylesheet sets
       `.choose_us { max-width: unset !important }` — deliberate in their layout,
       ruinous in ours: the screenshot's natural width is 1600px, the column is
       ~500px, and the image ran 480px past the right edge of the viewport and
       under the copy beside it. Measured: `x = 380 … 1980` in a 1500px window.
       It is a column image; it gets the column's width.

   (b) The feature rows. See the renderer: the tile replaces a solid blue disc. */
/* (c) ⛔ AND THE RATIO IS THE SOURCE MATERIAL'S, NOT THE COLUMN'S. This block
       used to set `4 / 3`, and `5 / 6` above 1200px, so the picture would fill
       the column's height beside the copy. That is a layout wish imposed on a
       photograph: every screenshot on this site is exactly 1600x900 — all
       eighteen of them, checked — so a 5/6 box with `object-fit: cover` threw
       away about 62% of the width and cut the BPMN diagram through the middle of
       an element. Measured on the new AI section: 521x620 for a 1600x900 source.
       A product screenshot that is cropped until it cannot be read is worse than
       no screenshot. The box takes the picture's ratio; the row already centres
       it against the taller copy column. */
.ds-app .choose-section .choose_us {
  width: 100%;
  max-width: 100% !important;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
.ds-app .ds-feature__icon {
  width: 42px; height: 42px; border-radius: 12px;
  background: rgba(var(--p2), .10);
  transition: background .25s ease;
}
.ds-app .ds-feature:hover .ds-feature__icon { background: rgba(var(--p2), .18); }
.ds-app .ds-features .ds-feature p { max-width: 54ch; }

/* -- 28 ------------------------------------------------------------------
   THREE LANGUAGES, ONE PAGE, NO CALL.

   The markup is rendered at build time and lives in the page, so the locale can no
   longer be chosen by asking a rule — the page has to arrive carrying all three and
   show one. Each is boxed in `.ds-lng[data-lng]`, and the box that matches
   `<html lang>` is the one that displays.

   ⛔ It keys off `<html lang>` and NOT off a class on `.ds-app`, because ds-kit sets
   `documentElement.lang` the moment it loads — in the head, before the body is
   parsed. A rule keyed to the app root would only match after the body existed, and
   the visitor would watch English repaint into their own language.

   The last rule is the no-JS floor: with no `lang` at all, English shows. */
.ds-app .ds-lng { display: none; }
html[lang^="en"] .ds-app .ds-lng[data-lng="en_US"],
html[lang^="ru"] .ds-app .ds-lng[data-lng="ru_RU"],
html[lang^="hy"] .ds-app .ds-lng[data-lng="hy_AM"],
html:not([lang]) .ds-app .ds-lng[data-lng="en_US"] { display: block; }

/* -- 30 ------------------------------------------------------------------
   ⛔ THE CONSOLE'S SKIN REACHES INTO THE SITE, WITH `!important`.

   The demo form's fields rendered near-BLACK on a white card in the light
   theme. Nothing in this file asked for that: §5 paints them `rgba(var(--p7),
   .6)`, and `--p7` resolves to 233,239,247 on `.ds-app` — measured, light. The
   painter is the PLATFORM: the console's skin stylesheet (`dark.css`, chosen by
   `nct.skin` in localStorage, which is "Dark" here) carries

       .form-control, .form-select, input[type="text"], input[type="email"], …
       { background-image: linear-gradient(135deg, #0a111e, #04080f) !important;
         background-color: initial !important;
         border: 3px solid rgb(54,84,132) !important; … }

   Two things follow, and both had to be answered here.

   (a) It is `!important`, so neither specificity nor order can win. The only
       answer is `!important` back, scoped to `.ds-app` so the console's own
       screens keep their skin untouched.
   (b) It forces a background-IMAGE, not a colour. `background: <colour>` leaves
       that gradient in place and paints underneath it — which is why §5 looked
       like it had simply been ignored. `background-image: none` is the rule that
       actually removes it.

   The skin is a user setting that the site cannot see and must not depend on: a
   visitor with `nct.skin = "Dark"` and the site in light mode is a normal state,
   not a corner case. Everything below is stated in palette tokens, so the field
   is correct in both site themes under either skin. */
.ds-app .form-control,
.ds-app .form-select,
.ds-app textarea,
.ds-app select,
.ds-app input[type="text"],
.ds-app input[type="email"],
.ds-app input[type="tel"],
.ds-app input[type="url"],
.ds-app input[type="search"],
.ds-app input[type="number"],
.ds-app input[type="password"] {
  background-image: none !important;
  background-color: rgba(var(--p7), .6) !important;
  border: 1px solid rgba(var(--p4), .22) !important;
  color: rgba(var(--p8), 1) !important;
  box-shadow: none !important;
}
.ds-app .form-control:focus,
.ds-app .form-select:focus,
.ds-app textarea:focus,
.ds-app select:focus,
.ds-app input[type="text"]:focus,
.ds-app input[type="email"]:focus,
.ds-app input[type="tel"]:focus,
.ds-app input[type="url"]:focus,
.ds-app input[type="search"]:focus,
.ds-app input[type="number"]:focus,
.ds-app input[type="password"]:focus {
  background-color: rgba(var(--p7), .92) !important;
  border-color: rgba(var(--p1), .75) !important;
  box-shadow: 0 0 0 4px rgba(var(--p1), .16) !important;
  outline: 0 !important;
}
.ds-app .ds-field.is-invalid .form-control { border-color: rgba(var(--p3), .8) !important; }

/* ⛔ AND THE PLACEHOLDER. Measured on the stand it came out rgb(138,157,180)
   on a near-white field — about 2.6:1, a hint rather than a label, and not what
   §5 asks for. Something in the console's cascade wins there too, so this one is
   stated with the rest of them. */
.ds-app .form-control::placeholder,
.ds-app textarea::placeholder,
.ds-app input[type="text"]::placeholder,
.ds-app input[type="email"]::placeholder,
.ds-app input[type="tel"]::placeholder {
  color: rgba(var(--p4), .92) !important;
  opacity: 1 !important;
}

/* ⛔ TWO ARROWS ON EVERY SELECT. The console's `common.css` carries
   `.form-control { appearance: auto !important }`, which puts the NATIVE arrow
   back on top of the one drawn below — visible on /demo as a double chevron in
   "Interested in". `appearance: none` is not optional here, and it has to shout
   as loudly as they do. The drawn arrow is a background-image, so it also has to
   be restated after the `background-image: none` above erases it. */
.ds-app select,
.ds-app select.form-control,
.ds-app .form-select {
  -webkit-appearance: none !important;
     -moz-appearance: none !important;
          appearance: none !important;
  padding-right: 44px !important;
  background-image:
    linear-gradient(45deg, transparent 50%, rgba(var(--p4), 1) 50%),
    linear-gradient(135deg, rgba(var(--p4), 1) 50%, transparent 50%) !important;
  background-position: calc(100% - 22px) calc(50% + 2px), calc(100% - 16px) calc(50% + 2px) !important;
  background-size: 6px 6px, 6px 6px !important;
  background-repeat: no-repeat !important;
}
.ds-app select::-ms-expand { display: none !important; }

/* ⛔ THE FOOTER FIELD IS THE EXCEPTION. It is not a card field on the page
   ground — it is cut into the dark footer (§21a), so it keeps its translucent
   white and its own hairline in BOTH themes. Without this it would be handed
   the light page surface above and turn into a white slab again. */
.ds-app .footer_section .newsletter .single-input input,
.ds-app .footer_section .newsletter .single-input input[type="email"] {
  background-image: none !important;
  background-color: transparent !important;
  border: 0 !important;
  box-shadow: none !important;
  color: rgba(var(--p6), .96) !important;
}

/* -- 31 ------------------------------------------------------------------
   THE FOOTER'S LAYOUT, looked at rather than inherited.

   (a) THE NEWSLETTER STRIP WAS TWO UNRELATED THINGS. Their row is
       `justify-content-between` inside a full-bleed container, so on a 2056px
       screen the heading sat at x=176 and the field at x=975 with 550px of
       nothing between them — and the field itself stretched to 563px, an empty
       trough for an address that needs a third of it. A form is a sentence: the
       label and the control have to read as one unit. The row now has a stated
       maximum and centres, and the field is capped and pulled to the right of
       its own column, so the two halves stay in conversation at any width.

   (b) IT ALSO HAD NO EDGE. `pb-120` put 120px of air under the strip and then
       the link columns began, with nothing to say the strip had ended. One
       hairline does the job the whitespace was failing to do, and the air comes
       down to something a reader can span.

   (c) THE CLOSING WORDMARK COULD NOT BE TRUSTED WITH ITS OWN SIZE. `display-six`
       is a fixed 144px; whatever string arrives is what wraps. The copy is now
       three words (seed: `tagline`), but a translation is free to be longer, so
       the size is tied to the viewport instead of the stylesheet and the line is
       allowed to shrink rather than stack. Armenian is the long one — it sets
       the lower bound. */
/* ⛔ NO max-width HERE. Capping the strip and centring it was the first attempt,
   and it put the hairline on a DIFFERENT grid from the three columns below: the
   rule ran 300→1204 while "Follow us" started at 118 and "Information" ended at
   1445. A divider that does not align with what it divides is worse than none.
   The strip keeps the columns' own container; only the FIELD is capped, and it
   is pushed to the right edge, so the label sits over "Follow us" and the field
   over "Information". */
.ds-app .footer_section .newsletter {
  align-items: center;
  padding-bottom: 56px !important;      /* their pb-120 is a utility, so: !important */
  border-bottom: 1px solid rgba(var(--p6), .12);
  margin-bottom: 56px;
}
.ds-app .footer_section .newsletter .single-input {
  max-width: 420px;
  margin-left: auto;
}
.ds-app .footer_section .newsletter .content-area h4 { line-height: 1.2; }

/* ⛔ THE COPYRIGHT WAS ALL BUT INVISIBLE. The bottom bar puts the copyright on
   the left and Terms / Privacy on the right; the links came out bright and the
   copyright beside them nearly vanished into the ground — the same row, two
   different contrasts, which reads as a rendering fault rather than a hierarchy.
   The copyright is secondary, so it is quieter than the links ON PURPOSE — but
   legibly so, and the two now differ by one deliberate step. */
.ds-app .footer_section .container-fluid > span,
.ds-app .footer_section .container-fluid > span span { color: rgba(var(--p6), .62) !important; }
.ds-app .footer_section .container-fluid .d-flex a { color: rgba(var(--p6), .86) !important; }
.ds-app .footer_section .container-fluid .d-flex a:hover { color: rgba(var(--p6), 1) !important; }

/* the first column's sentence: a measure, not a full-width line */
.ds-app .footer_section [data-ds-footer-blurb] {
  max-width: 34ch;
  line-height: 1.6;
  color: rgba(var(--p6), .72) !important;
}

/* the three columns, and the one that had nothing in it */
.ds-app .footer_section .social_info { margin-top: 0 !important; }
.ds-app .footer_section .social_info .contact_icon {
  width: 44px; height: 44px;
  transition: transform .15s ease, background-color .2s ease;
}
.ds-app .footer_section .social_info .contact_icon:hover { transform: translateY(-2px); }

/* the closing wordmark */
.ds-app .footer_section [data-ds-tagline] {
  display: block;
  font-size: clamp(22px, 6.4vw, 118px);
  line-height: 1.02;
  letter-spacing: -.02em;
  text-wrap: balance;
  opacity: .9;
}

@media (max-width: 991px) {
  .ds-app .footer_section .newsletter { padding-bottom: 36px !important; margin-bottom: 36px; }
  .ds-app .footer_section .newsletter .single-input { max-width: none; margin-left: 0; }
}

/* -- 32 ------------------------------------------------------------------
   THE SCREENSHOT FOLLOWS THE THEME.

   Every product shot ships twice — `x.jpg` from the platform in its light skin
   and `x-dark.jpg` from the same screen in its dark one. `DS.assets()` puts both
   on the node as `--ds-shot-l` / `--ds-shot-d`; these two rules decide which one
   `--ds-shot` resolves to, and the hero's scrim reads `--ds-shot` exactly as it
   did before. Nothing in the renderers changed.

   ⛔ IT HAS TO BE DECLARED ON THE NODE ITSELF. `--ds-shot-l` is an INLINE custom
   property on the slide, and a `var()` only sees the custom properties of the
   element the declaration sits on — putting `--ds-shot: var(--ds-shot-l)` on
   `.ds-app` would resolve it against the APP's variables, where those two do not
   exist, and every hero would go blank. Hence the descendant selector.

   ⛔ AND IT CANNOT BE SET INLINE BY THE SCRIPT. An inline declaration outranks
   any stylesheet rule, so if `DS.assets()` wrote `--ds-shot` itself the theme
   toggle could never change it. The script writes only the two sources; the
   choice belongs to CSS, which is why flipping `.dark` swaps the picture with no
   JS, no second request on the first paint, and no flash. */
.ds-app [data-ds-bg] { --ds-shot: var(--ds-shot-l, none); }
.ds-app.dark [data-ds-bg] { --ds-shot: var(--ds-shot-d, var(--ds-shot-l, none)); }

/* -- 33 ------------------------------------------------------------------
   THE ACCOUNT CLUSTER. It sits in the header's right-hand row beside the
   language control and the theme toggle, and it borrows their geometry on
   purpose — a fourth shape there would read as a fourth idea. Signed out it is
   a pill, because it is the one thing on that row a visitor is meant to press;
   signed in it is a circle like its neighbours, because then it is a utility.

   The menu reuses the language menu's surface (§26) rather than inventing a
   second popover: same radius, same elevation, same hairline, so the header has
   one vocabulary. */
.ds-app .ds-account { display: inline-flex; }
.ds-app .ds-account__login {
  padding: 9px 18px;
  border-radius: 999px;
  border: 1px solid rgba(var(--p1), .55);
  background: rgba(var(--p1), .12);
  color: rgba(var(--p8), 1);
  font-size: 14px;
  line-height: 1;
  white-space: nowrap;
  transition: background-color .2s ease, border-color .2s ease;
}
.ds-app .ds-account__login:hover {
  background: rgba(var(--p1), .22);
  border-color: rgba(var(--p1), .8);
  color: rgba(var(--p8), 1);
}
.ds-app .ds-account__login i { font-size: 17px; }

.ds-app .ds-account__btn {
  width: 44px; height: 44px;
  padding: 0;
  border: 1px solid rgba(var(--p4), .28);
  background: transparent;
  color: rgba(var(--p8), 1);
  font-size: 21px;
  transition: border-color .2s ease, background-color .2s ease;
}
.ds-app .ds-account__btn:hover { border-color: rgba(var(--p1), .7); background: rgba(var(--p1), .10); }

.ds-app .ds-account__menu {
  position: absolute; top: calc(100% + 10px); right: 0;
  min-width: 210px;
  margin: 0; padding: 6px;
  list-style: none;
  border-radius: 14px;
  background: rgba(var(--p5), .98);
  border: 1px solid rgba(var(--p4), .18);
  box-shadow: 0 18px 44px rgba(var(--p9), .22);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity .16s ease, transform .16s ease, visibility .16s;
  z-index: 60;
}
.ds-app .ds-account__btn[aria-expanded="true"] + .ds-account__menu {
  opacity: 1; visibility: visible; transform: none;
}
.ds-app .ds-account__menu a {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
  border-radius: 9px;
  font-size: 14px;
  color: rgba(var(--p8), .92);
  white-space: nowrap;
}
.ds-app .ds-account__menu a:hover { background: rgba(var(--p1), .12); color: rgba(var(--p8), 1); }
.ds-app .ds-account__menu i { font-size: 17px; opacity: .75; }

.ds-app .ds-account__menu .ds-account__edit {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 9px;
  font-size: 14px; font-weight: 600;
  color: rgba(var(--p1), 1) !important;
  white-space: nowrap;
}
.ds-app .ds-account__menu .ds-account__edit:hover { background: rgba(var(--p1), .12); }
.ds-app .ds-account__menu .ds-account__edit i { font-size: 17px; opacity: 1; }
.ds-app .ds-account__menu .ds-account__sep {
  height: 1px; margin: 6px 8px;
  background: rgba(var(--p4), .16);
}
/* the account button says, quietly, that editing is on */
.ds-app .ds-account__btn.is-editing {
  border-color: rgba(var(--p2), .75);
  background: rgba(var(--p2), .12);
  color: rgba(var(--p2), 1);
}

@media (max-width: 575px) {
  .ds-app .ds-account__login { padding: 8px 14px; font-size: 13px; }
  .ds-app .ds-account__login span { display: none; }   /* the icon carries it */
  .ds-app .ds-account__btn { width: 40px; height: 40px; font-size: 19px; }
}

/* -- 34 ------------------------------------------------------------------
   THE OFF-CANVAS PANEL. It holds the site's whole sitemap and it was a column
   of ghosts.

   ⛔ THE REPAINT DID IT, AND IT COULD ONLY HAPPEN TO A PANEL. Their drawer is a
   DARK slab, so every link inside it is `n1-color` — light ink for a dark ground.
   `20_site_assets.py` substitutes this palette into their stylesheet, and the
   panel's own `linear-gradient(180deg, rgba(var(--p5),1) …)` became the LIGHT page
   ground. A background-IMAGE always paints over a background-COLOR, so the navy
   still computed on the element (`rgb(16,27,44)`) never shows, and the light ink
   lands on a near-white surface: measured `rgb(203,216,230)` on `rgb(251,252,254)`.
   Every other surface survived the repaint because it takes its colour from a
   variable; this one had a gradient hard-coded over it.

   ⛔ AND THERE WAS NO HIERARCHY UNDERNEATH THE CONTRAST BUG. A group and its
   children differed by 2px of type and nothing else, while `//GET IN TOUCH` sat
   above them at 24px bold — the least important thing on the panel was the
   loudest thing on it. Twenty-five links, one weight, one colour, no grouping.

   So: the surface keeps the page ground (it is correct in both themes), the INK
   comes from the palette instead of from their dark-panel assumption, and the
   three levels are told apart the way a menu tells them apart — the group is a
   small muted label, the item is the thing you read, the current one is marked. */
.ds-app .header-section .nav-items-wrapper {
  /* ⛔ AND THE COLOUR UNDER THE GRADIENT HAS TO AGREE WITH IT. The element kept
     their `background-color: rgb(16,27,44)` with our light gradient painted over
     the top: invisible to a reader, but every tool that asks an element for its
     background — including a contrast audit — gets the navy and reports white
     text on white. Thirty-five false positives came from this one line. */
  border-left: 1px solid rgba(var(--p4), .18);
  box-shadow: -24px 0 60px rgba(var(--p9), .14);
}
/* ⛔ AND IT TAKES A MORE SPECIFIC SELECTOR, NOT A LATER ONE. Their navy comes
   from `.ds-app .header-section.header-menu .sidebar-items .single-item …` —
   four classes — so a three-class rule loses however far down the file it sits.
   Matched class for class, plus the element itself. */
.ds-app .header-section.header-menu .sidebar-items .single-item .nav-items-wrapper,
.ds-app .header-section .sidebar-items .single-item .nav-items-wrapper {
  background-color: rgba(var(--p5), 1);
}

.ds-app .nav-items-wrapper .ds-drawer__title {
  margin: 0 0 20px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(var(--p4), .85);
}
.ds-app .nav-items-wrapper .ds-drawer__list,
.ds-app .nav-items-wrapper .ds-drawer__kids { list-style: none; margin: 0; padding: 0; }

.ds-app .nav-items-wrapper .ds-drawer__section + .ds-drawer__section {
  margin-top: 22px;
  padding-top: 22px;
  border-top: 1px solid rgba(var(--p4), .12);
}
.ds-app .nav-items-wrapper .ds-drawer__group {
  display: block;
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(var(--p4), .96) !important;   /* 12px needs 4.5:1, and .8 gave 4.3 */
}
.ds-app .nav-items-wrapper .ds-drawer__group:hover { color: rgba(var(--p1), 1) !important; }

.ds-app .nav-items-wrapper .ds-drawer__item {
  display: block;
  padding: 7px 10px;
  margin-left: -10px;
  border-radius: 8px;
  font-size: 15px;
  line-height: 1.35;
  color: rgba(var(--p8), .9) !important;
  transition: background-color .15s ease, color .15s ease;
}
.ds-app .nav-items-wrapper .ds-drawer__item:hover {
  background: rgba(var(--p1), .10);
  color: rgba(var(--p8), 1) !important;
}
.ds-app .nav-items-wrapper [aria-current="page"] {
  color: rgba(var(--p1), 1) !important;
  font-weight: 600;
}
.ds-app .nav-items-wrapper .ds-drawer__item[aria-current="page"] { background: rgba(var(--p1), .12); }

/* ⛔ THE TWO SHOUTING HEADINGS. `//GET IN TOUCH` and `//FOLLOW US` are `h5` in
   their markup but render at 24px bold, louder than the navigation they sit
   under. They are labels, so they are set like the other label on the panel. */
.ds-app .nav-items-wrapper h5 {
  font-size: 12px !important;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(var(--p4), .8) !important;
  margin-bottom: 10px;
}
.ds-app .nav-items-wrapper .address,
.ds-app .nav-items-wrapper .single-box { color: rgba(var(--p8), .9); }
.ds-app .nav-items-wrapper .address a { color: rgba(var(--p8), .9) !important; }
.ds-app .nav-items-wrapper .address a:hover { color: rgba(var(--p1), 1) !important; }
.ds-app .nav-items-wrapper .close-btn { color: rgba(var(--p8), .8); }

/* -- 35 ------------------------------------------------------------------
   ONE FIXED GROUND, AND ONLY ONE.

   The page was a stack of bands that all shared `--p7`: `numbers`, `products`,
   `forms` and `industries` are the same flat colour one after another, which is
   why the middle of the page reads as undercooked — nothing on it is an EVENT.

   `#products` gets the fixed ground, and the choice is not arbitrary:
     · it is the tallest section on the page (1088px measured) — a fixed ground
       needs travel before the eye reads it as fixed rather than as a texture;
     · its content is six CARDS, opaque panels that float clearly over something
       moving. Body copy laid straight onto a textured ground would only get
       harder to read, which rules out `why` (877px, and its right half is
       already a screenshot competing for attention);
     · it sits in the upper third, so the effect lands once, early, and the rest
       of the page stays calm. A fixed ground used twice is not an event any more.

   It is built from the palette and the site's own motif — the perspective grid
   the hero draws and the footer answers (§28) — not from a photograph. So it
   recolours with the theme instead of needing two assets, and there is nothing
   to download.

   ⛔ `background-attachment: fixed` IS DESKTOP-ONLY, DELIBERATELY. iOS ignores it
   and paints the layer at the wrong size, and on a touch device it forces a
   full-surface repaint on every scroll frame. The media query asks for a real
   pointer and a wide viewport; everything else gets the same ground, scrolling
   normally, which looks intentional rather than broken. Reduced motion gets the
   same treatment, because "fixed" IS the motion here. */
/* ⛔ THE MOTIF IS THE PRODUCT, NOT A TEXTURE. A neutral grid is what a template
   ships when it has nothing to say. These three sections each get the thing they
   are ABOUT, drawn in line: a BPMN route behind the products, a chart behind the
   numbers, a form behind the forms. Same vocabulary as the hero's motifs, and
   the reason the effect is worth having at all — the visitor scrolls THROUGH a
   picture of the software.

   ⛔ THEY ARE MASKS OF COLOUR, NOT PICTURES. Each is an inline SVG data-URI, a
   few hundred bytes, no request, nothing to 404. The stroke colour is baked, so
   each has a dark-theme twin below — a data-URI cannot read a CSS variable, and
   two declarations are cheaper than a runtime that redraws them. */
.ds-app #products {
  position: relative;
  background-color: rgba(var(--p7), 1);
  background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27720%27%20height%3D%27240%27%20viewBox%3D%270%200%20720%20240%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20stroke-width%3D%271.75%27%20stroke-linecap%3D%27round%27%3E%3Cpath%20d%3D%27M0%20120h40%27%2F%3E%3Crect%20x%3D%2740%27%20y%3D%2797%27%20width%3D%27104%27%20height%3D%2746%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M144%20120h34%27%2F%3E%3Cpath%20d%3D%27M178%20120l22-22%2022%2022-22%2022z%27%2F%3E%3Cpath%20d%3D%27M222%20120h30%27%2F%3E%3Cpath%20d%3D%27M200%2098V56h132M200%20142v42h132%27%2F%3E%3Crect%20x%3D%27332%27%20y%3D%2735%27%20width%3D%2796%27%20height%3D%2742%27%20rx%3D%2710%27%2F%3E%3Crect%20x%3D%27332%27%20y%3D%27163%27%20width%3D%2796%27%20height%3D%2742%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M428%2056h44v64M428%20184h44v-64%27%2F%3E%3Crect%20x%3D%27252%27%20y%3D%2797%27%20width%3D%27104%27%20height%3D%2746%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M472%20120h42%27%2F%3E%3Ccircle%20cx%3D%27530%27%20cy%3D%27120%27%20r%3D%2713%27%2F%3E%3Cpath%20d%3D%27M543%20120h37%27%2F%3E%3Crect%20x%3D%27580%27%20y%3D%2797%27%20width%3D%27104%27%20height%3D%2746%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M684%20120h36%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20opacity%3D%27.5%27%3E%3Crect%20x%3D%2756%27%20y%3D%27112%27%20width%3D%2756%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%2756%27%20y%3D%27122%27%20width%3D%2734%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27268%27%20y%3D%27112%27%20width%3D%2756%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27268%27%20y%3D%27122%27%20width%3D%2740%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27596%27%20y%3D%27112%27%20width%3D%2756%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27596%27%20y%3D%27122%27%20width%3D%2730%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"),
    radial-gradient(90% 70% at 18% 8%, rgba(var(--p1), var(--ds-tint-cool)) 0%, rgba(var(--p1), 0) 62%),
    radial-gradient(70% 60% at 88% 92%, rgba(var(--p2), var(--ds-tint-warm)) 0%, rgba(var(--p2), 0) 60%),
    linear-gradient(180deg, rgba(var(--p7), .92) 0%, rgba(var(--p7), 1) 100%);
  /* ⛔ REPEAT-X ONLY, AND THE TILE IS SEAMLESS. Tiled both ways it was wallpaper:
     the same route three times across and again below, cut mid-shape at every
     edge. The drawing now enters at x=0 and leaves at x=720 on the same line, so
     one row of it reads as a SINGLE process running the width of the page. */
  background-size: 720px 240px, cover, cover, cover;
  background-repeat: repeat-x, no-repeat, no-repeat, no-repeat;
  background-position: center 64px, center, center, center;
}
.ds-app.dark #products { background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27720%27%20height%3D%27240%27%20viewBox%3D%270%200%20720%20240%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20stroke-width%3D%271.75%27%20stroke-linecap%3D%27round%27%3E%3Cpath%20d%3D%27M0%20120h40%27%2F%3E%3Crect%20x%3D%2740%27%20y%3D%2797%27%20width%3D%27104%27%20height%3D%2746%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M144%20120h34%27%2F%3E%3Cpath%20d%3D%27M178%20120l22-22%2022%2022-22%2022z%27%2F%3E%3Cpath%20d%3D%27M222%20120h30%27%2F%3E%3Cpath%20d%3D%27M200%2098V56h132M200%20142v42h132%27%2F%3E%3Crect%20x%3D%27332%27%20y%3D%2735%27%20width%3D%2796%27%20height%3D%2742%27%20rx%3D%2710%27%2F%3E%3Crect%20x%3D%27332%27%20y%3D%27163%27%20width%3D%2796%27%20height%3D%2742%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M428%2056h44v64M428%20184h44v-64%27%2F%3E%3Crect%20x%3D%27252%27%20y%3D%2797%27%20width%3D%27104%27%20height%3D%2746%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M472%20120h42%27%2F%3E%3Ccircle%20cx%3D%27530%27%20cy%3D%27120%27%20r%3D%2713%27%2F%3E%3Cpath%20d%3D%27M543%20120h37%27%2F%3E%3Crect%20x%3D%27580%27%20y%3D%2797%27%20width%3D%27104%27%20height%3D%2746%27%20rx%3D%2710%27%2F%3E%3Cpath%20d%3D%27M684%20120h36%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20opacity%3D%27.5%27%3E%3Crect%20x%3D%2756%27%20y%3D%27112%27%20width%3D%2756%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%2756%27%20y%3D%27122%27%20width%3D%2734%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27268%27%20y%3D%27112%27%20width%3D%2756%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27268%27%20y%3D%27122%27%20width%3D%2740%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27596%27%20y%3D%27112%27%20width%3D%2756%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3Crect%20x%3D%27596%27%20y%3D%27122%27%20width%3D%2730%27%20height%3D%273.5%27%20rx%3D%271.75%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"),
    radial-gradient(90% 70% at 18% 8%, rgba(var(--p1), var(--ds-tint-cool)) 0%, rgba(var(--p1), 0) 62%),
    radial-gradient(70% 60% at 88% 92%, rgba(var(--p2), var(--ds-tint-warm)) 0%, rgba(var(--p2), 0) 60%),
    linear-gradient(180deg, rgba(var(--p7), .92) 0%, rgba(var(--p7), 1) 100%); }

/* ⛔ FIXED IS DESKTOP-ONLY. iOS ignores `background-attachment: fixed` and paints
   the layer at the wrong size; on touch it repaints the whole surface per frame.
   And "fixed" IS the motion here, so reduced motion opts out of it too. */
@media (min-width: 992px) and (hover: hover) and (prefers-reduced-motion: no-preference) {
  /* ⛔ A FIXED LAYER IS POSITIONED AGAINST THE VIEWPORT, NOT THE SECTION. Left at
     `center 64px` the band was drawn 64px from the top of the SCREEN, so the only
     moment the section's box overlapped it was as its top edge crossed the very
     top of the window — in practice the process line was invisible. Centred, it
     sits across the middle of the screen and the section reveals it as it passes:
     the drawing stays, the cards travel over it. Which is the whole effect. */
  .ds-app #products {
    background-attachment: fixed, fixed, fixed, fixed;
    background-position: center center, center, center, center;
  }
}

/* the cards have to read as ABOVE it, or the whole thing is just noise */
/* ⛔ `--ds-surface`, AND NEITHER `--p5` NOR `--p6` ON ITS OWN WILL DO. These cards
   are deliberately translucent so the frozen motif shows through, and they were
   tinted with `--p5` — which is THEME-AWARE: near-white in light, near-black in
   dark, so one token gave each skin the right card. §45 moved `--p5` down to
   240,244,250 to make room for white as a surface, and this card sank with it
   (measured .901 over a section at .818). Swapping it to `--p6` fixed the light
   skin and BROKE THE DARK ONE THE SAME DAY: `--p6` is 255,255,255 in both, so the
   dark page grew six white blocks. Measured: `rgba(255,255,255,0.94)` on a
   near-black section.
   The token that was needed does not exist in their palette, so it is declared:
   "the colour a raised surface takes in THIS skin". */
.ds-app #products .single-item,
.ds-app #products .service-item,
.ds-app #products [class*="card"] {
  background-color: rgba(var(--ds-surface), .94);
  backdrop-filter: blur(3px);
  border-color: rgba(var(--p4), .16) !important;
  box-shadow: 0 10px 30px rgba(var(--p9), .10);
}

/* ⛔ AND THE ICON CHIP HAD TO STOP BEING A PATCH. The template gives each card's
   icon an OPAQUE rounded square. On their flat white card nobody notices it; on
   a card that is now 94% translucent over a drawing, a solid tile is the one
   opaque thing in the composition and the eye goes straight to it — the owner
   saw it immediately.

   ⛔ AND IT WAS CARRYING THE WRONG ACCENT. Measured: the chip is
   `rgba(22,104,171,.12)` — BLUE — with a GREEN icon inside it. Two accents in a
   56px square, which is what actually made it noisy; a soft filled blob with no
   edge, over a translucent card, over a line drawing, reads as a smudge rather
   than as a component. The chip is now a neutral tint of the card's own ink with
   a hairline — a PLACE for the icon — and the green belongs to the icon alone.
   Hover is the only time the chip takes the accent, which is what hover is for. */
.ds-app #products .single-item .img-area {
  background-color: rgba(var(--p4), .07) !important;
  background-image: none !important;
  border: 1px solid rgba(var(--p4), .16);
  box-shadow: none !important;
  transition: background-color .18s ease, border-color .18s ease;
}
.ds-app #products .single-item:hover .img-area {
  background-color: rgba(var(--p2), .10) !important;
  border-color: rgba(var(--p2), .28);
}

/* -- 36 ------------------------------------------------------------------
   AND THE BAND ABOVE IT STOPS BEING FLAT. `#numbers` carries the page's only
   hard numbers and sat on the same `--p7` as three sections below it. It does
   not get the fixed ground — that is `#products`' one job — it gets a quiet
   directional wash so the two read as a pair (the claim, then the range) rather
   than as two identical stripes. */
/* ⛔ AND THIS ONE CARRIES NO GREEN AT ALL. `#numbers` exists for four green
   numerals; a green ground under them is the ground arguing with the content.
   It gets the cool wash only, which is what makes the numerals read as the
   accent rather than as part of the band. */
.ds-app #numbers {
  background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27560%27%20height%3D%27300%27%20viewBox%3D%270%200%20560%20300%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20stroke-width%3D%271.75%27%3E%3Cpath%20d%3D%27M28%20276h504M28%20276V36%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20opacity%3D%27.42%27%3E%3Crect%20x%3D%2756%27%20y%3D%27196%27%20width%3D%2734%27%20height%3D%2780%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27140%27%20y%3D%27160%27%20width%3D%2734%27%20height%3D%27116%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27224%27%20y%3D%27182%27%20width%3D%2734%27%20height%3D%2794%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27308%27%20y%3D%27112%27%20width%3D%2734%27%20height%3D%27164%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27392%27%20y%3D%27146%27%20width%3D%2734%27%20height%3D%27130%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27476%27%20y%3D%2788%27%20width%3D%2734%27%20height%3D%27188%27%20rx%3D%275%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20stroke-width%3D%272.25%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M73%20232l84-46%2084%2022%2084-70%2084%2034%2084-58%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20stroke-width%3D%271.75%27%3E%3Ccircle%20cx%3D%2773%27%20cy%3D%27232%27%20r%3D%274.5%27%2F%3E%3Ccircle%20cx%3D%27241%27%20cy%3D%27208%27%20r%3D%274.5%27%2F%3E%3Ccircle%20cx%3D%27493%27%20cy%3D%27114%27%20r%3D%274.5%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"),
    radial-gradient(70% 90% at 78% 6%, rgba(var(--p1), calc(var(--ds-tint-cool) * .55)) 0%, rgba(var(--p1), 0) 58%),
    linear-gradient(180deg, rgba(var(--p5), 1) 0%, rgba(var(--p7), .75) 100%);
  background-repeat: no-repeat, no-repeat, no-repeat;
  /* ⛔ AND NOT CLIPPED. A bar sliced by the section edge reads as a rendering
     fault, not as a crop. It sits clear of the copy and fully inside its box. */
  background-size: 560px 300px, cover, cover;
  background-position: left 3% bottom 56px, center, center;
}
.ds-app.dark #numbers { background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27560%27%20height%3D%27300%27%20viewBox%3D%270%200%20560%20300%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20stroke-width%3D%271.75%27%3E%3Cpath%20d%3D%27M28%20276h504M28%20276V36%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20opacity%3D%27.42%27%3E%3Crect%20x%3D%2756%27%20y%3D%27196%27%20width%3D%2734%27%20height%3D%2780%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27140%27%20y%3D%27160%27%20width%3D%2734%27%20height%3D%27116%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27224%27%20y%3D%27182%27%20width%3D%2734%27%20height%3D%2794%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27308%27%20y%3D%27112%27%20width%3D%2734%27%20height%3D%27164%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27392%27%20y%3D%27146%27%20width%3D%2734%27%20height%3D%27130%27%20rx%3D%275%27%2F%3E%3Crect%20x%3D%27476%27%20y%3D%2788%27%20width%3D%2734%27%20height%3D%27188%27%20rx%3D%275%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20stroke-width%3D%272.25%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M73%20232l84-46%2084%2022%2084-70%2084%2034%2084-58%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20stroke-width%3D%271.75%27%3E%3Ccircle%20cx%3D%2773%27%20cy%3D%27232%27%20r%3D%274.5%27%2F%3E%3Ccircle%20cx%3D%27241%27%20cy%3D%27208%27%20r%3D%274.5%27%2F%3E%3Ccircle%20cx%3D%27493%27%20cy%3D%27114%27%20r%3D%274.5%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"),
    radial-gradient(70% 90% at 78% 6%, rgba(var(--p1), calc(var(--ds-tint-cool) * .55)) 0%, rgba(var(--p1), 0) 58%),
    linear-gradient(180deg, rgba(var(--p5), 1) 0%, rgba(var(--p7), .75) 100%); }

/* the forms band answers with a form. It is a SINGLE tile, placed — not tiled —
   so it reads as one drawing sitting behind the copy rather than as wallpaper. */
.ds-app #forms {
  background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27520%27%20height%3D%27320%27%20viewBox%3D%270%200%20520%20320%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20stroke-width%3D%271.75%27%3E%3Crect%20x%3D%2724%27%20y%3D%2740%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%2740%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27118%27%20width%3D%27472%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27196%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%27196%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Cpath%20d%3D%27M470%20210l-11%2011%2011%2011%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27272%27%20width%3D%2721%27%20height%3D%2721%27%20rx%3D%276%27%2F%3E%3Cpath%20d%3D%27M29%20283l5%205%208-10%27%2F%3E%3Crect%20x%3D%27366%27%20y%3D%27266%27%20width%3D%27130%27%20height%3D%2736%27%20rx%3D%2718%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27rgba%288%2C21%2C37%2C0.062%29%27%20opacity%3D%27.5%27%3E%3Crect%20x%3D%2724%27%20y%3D%2722%27%20width%3D%2760%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%2722%27%20width%3D%2776%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27100%27%20width%3D%2794%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27178%27%20width%3D%2752%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%27178%27%20width%3D%2768%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%2756%27%20y%3D%27279%27%20width%3D%27112%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"),
    linear-gradient(180deg, rgba(var(--p7), .6) 0%, rgba(var(--p7), 1) 100%);
  background-repeat: no-repeat, no-repeat;
  background-size: 560px 380px, cover;
  background-position: right -60px center, center;
}
.ds-app.dark #forms { background-image:
    url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27520%27%20height%3D%27320%27%20viewBox%3D%270%200%20520%20320%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20stroke-width%3D%271.75%27%3E%3Crect%20x%3D%2724%27%20y%3D%2740%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%2740%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27118%27%20width%3D%27472%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27196%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%27196%27%20width%3D%27222%27%20height%3D%2742%27%20rx%3D%279%27%2F%3E%3Cpath%20d%3D%27M470%20210l-11%2011%2011%2011%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27272%27%20width%3D%2721%27%20height%3D%2721%27%20rx%3D%276%27%2F%3E%3Cpath%20d%3D%27M29%20283l5%205%208-10%27%2F%3E%3Crect%20x%3D%27366%27%20y%3D%27266%27%20width%3D%27130%27%20height%3D%2736%27%20rx%3D%2718%27%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27rgba%28233%2C239%2C247%2C0.085%29%27%20opacity%3D%27.5%27%3E%3Crect%20x%3D%2724%27%20y%3D%2722%27%20width%3D%2760%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%2722%27%20width%3D%2776%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27100%27%20width%3D%2794%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%2724%27%20y%3D%27178%27%20width%3D%2752%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%27274%27%20y%3D%27178%27%20width%3D%2768%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3Crect%20x%3D%2756%27%20y%3D%27279%27%20width%3D%27112%27%20height%3D%274.5%27%20rx%3D%272.25%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"),
    linear-gradient(180deg, rgba(var(--p7), .6) 0%, rgba(var(--p7), 1) 100%); }

/* -- 37 ------------------------------------------------------------------
   HOW MUCH ACCENT A GROUND MAY CARRY, AND IT IS NOT THE SAME IN THE TWO THEMES.

   The brand has two accents. Blue `--p1` is the structural one; green `--p2/--p3`
   is the one that MARKS things — the stat numerals, the tick in a list, the
   eyebrow over a heading. Painting a FIELD in the green broke that division: on
   the light theme `rgba(var(--p2), .14)` over `--p5` is a visible mint, and
   `#numbers` had two green layers and no blue at all, so the band went minty
   underneath the very numerals whose colour was supposed to be the accent. A
   ground that is the same hue as the marks on it cancels the marks.

   On the dark theme the same alpha is not the same picture: green over a near
   black reads as luminous rather than as a tint, and it can carry more.

   So the alphas are TOKENS, not literals, and they differ per theme. A ground
   asks for `--ds-tint-cool` (blue, the field) and `--ds-tint-warm` (green, a
   whisper) instead of choosing numbers at each call site — which is how the
   three grounds drifted apart in the first place.

   Light: green is almost absent (.045) — present enough to warm a corner, never
   enough to be seen as colour. Dark: it may be felt (.13). */
.ds-app {
  --ds-tint-cool: .10;
  --ds-tint-warm: .045;
}
.ds-app.dark {
  --ds-tint-cool: .22;
  --ds-tint-warm: .13;
}

/* -- 38 ------------------------------------------------------------------
   THE HEADER'S CIRCLE BUTTONS ARE TRANSPARENT.

   Measured in both themes, the theme toggle and the apps button were already
   IDENTICAL — `rgba(22,104,171,.1)` fill, `1px rgba(22,104,171,.2)` hairline, no
   pseudo-element, no shadow. Neither was transparent; both carried a 10% wash.
   A tinted disc beside a tinted disc beside the demo button gives the top-right
   corner three filled shapes competing with the one thing there that should be
   filled — the call to action. The fill goes; the hairline stays, so the target
   is still visible; the wash returns on hover, where it belongs. */
.ds-app .header-section .dark_btn,
.ds-app .header-section .ds-account__btn,
.ds-app .header-section .ds-lang-btn {
  background-color: transparent !important;
  background-image: none !important;
  border: 1px solid rgba(var(--p4), .28) !important;
  box-shadow: none !important;
  transition: background-color .18s ease, border-color .18s ease;
}
.ds-app .header-section .dark_btn:hover,
.ds-app .header-section .ds-account__btn:hover,
.ds-app .header-section .ds-lang-btn:hover {
  background-color: rgba(var(--p1), .10) !important;
  border-color: rgba(var(--p1), .45) !important;
}
/* ⛔ THE SQUARE WAS MINE. The apps button sits inside `div.single-item.target-item`
   — the template reuses `.single-item` for a CARD and for this 44px wrapper — and
   the light-theme elevation block paints `.ds-app:not(.dark) .single-item` solid
   `--p6`. So an opaque WHITE SQUARE was drawn behind a round transparent button,
   which is exactly what it looked like. Measured: the wrapper is 44x44,
   `border-radius: 0`, `background-color: rgb(255,255,255)`, while the button
   inside it is 44x44 at 50% with a hairline and no fill.
   A class that means two things needs the narrower one excluded by hand. */
.ds-app .header-section .single-item,
.ds-app .header-section .cmn-head {
  background-color: transparent !important;
  background-image: none !important;
  box-shadow: none !important;
  border: 0 !important;
}

/* the one exception: while editing is on, the account button says so */
.ds-app .header-section .ds-account__btn.is-editing {
  background-color: rgba(var(--p2), .12) !important;
  border-color: rgba(var(--p2), .70) !important;
}

/* -- 39 ------------------------------------------------------------------
   THE CONSOLE SKIN REACHES THE SITE, AND THE SITE MUST NOT CARE WHICH SKIN IT IS.

   `nctSkin` is an ACCOUNT setting — the platform's own "Change theme" (Standard,
   Dracula, Forest, Dark, Dark Blue) — and the platform injects that skin's
   stylesheet into every page it serves, the public site included. It has nothing
   to do with the site's light/dark, which is `.ds-app.dark`. So a visitor whose
   console skin is Dark reads the site in light mode with `dark.css` loaded over
   it. That is a normal state, not a corner case, and the site cannot fix it by
   asking: it has to be immune.

   Audited rather than guessed. Of the skin's rules that match anything inside
   `.ds-app`, exactly fifteen force a value, in three families:
     · `.form-control` (12 nodes) — answered in §30;
     · `.dropdown-menu` / `.dropdown-item` (12 and 54) — the site's own desktop
       navigation panels, answered here;
     · `[class*="border"]` / `.border` (6) — border COLOUR only, answered here.

   ⛔ AND THE VISIBLE FAULT WAS TEXT, NOT BACKGROUND. Measured in the light theme:
   `.dropdown-menu` came out `background rgba(251,252,254,.97)` — ours, correct —
   with `color: rgb(233,239,247)`, near-white ink on a near-white panel, inherited
   from a dark-panel assumption. Anything in a dropdown that is not an `a.dropdown-item`
   was invisible.

   ⛔ `base.css` IS NOT TOUCHED. It carries Bootstrap's utilities — `.d-flex`,
   `.w-100`, `.position-fixed` — with `!important`, and this site's markup is built
   on them. Only the SKIN is neutralised. */
.ds-app .dropdown-menu {
  color: rgba(var(--p8), .92) !important;
  border-color: rgba(var(--p4), .16) !important;
}
.ds-app .dropdown-menu .dropdown-item,
.ds-app .dropdown-item {
  background-image: none !important;
  color: rgba(var(--p8), .92) !important;
}
.ds-app .dropdown-menu .dropdown-item:hover,
.ds-app .dropdown-item:hover {
  background-color: rgba(var(--p1), .10) !important;
  color: rgba(var(--p8), 1) !important;
}
/* the skin repaints border COLOUR on anything whose class contains "border";
   ours are drawn from the palette and must stay that way */
.ds-app [class*="border"]:not([class*="border-0"]) {
  border-color: rgba(var(--p4), .18) !important;
}

/* -- 40 ------------------------------------------------------------------
   The footer's Information column now distinguishes a GROUP from a LINK: the
   group is text, so it needs to look like a label rather than like a link that
   refuses to work. Quieter, and it stops taking the hover it cannot honour. */
.ds-app .footer_section .ds-footer-group .footer_tag {
  color: rgba(var(--p6), .55) !important;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 11px !important;
}
.ds-app .footer_section .ds-footer-group .ds-bullet { opacity: .45; }
.ds-app .footer_section .ds-footer-group { cursor: default; }

/* -- 41 ------------------------------------------------------------------
   THE SITE COULD NOT BE USED WITH A KEYBOARD.

   Measured: fourteen of fourteen interactive elements — every nav link, every
   button, every field — computed `outline-style: none` with no box-shadow. You
   can tab through the site and never see where you are. Purchased themes do this
   routinely, because a default outline is ugly on a rounded button and removing
   it is quicker than designing one.

   ⛔ `:focus-visible`, NOT `:focus`. A ring on `:focus` fires on mouse clicks
   too, which is why the template removed it in the first place; `:focus-visible`
   is shown for keyboard and assistive navigation and stays out of the way of a
   pointer. The `:focus:not(:focus-visible)` line below re-states "no ring for a
   mouse" explicitly, so the rule reads as a decision rather than as a default.

   ⛔ TWO RINGS, NOT ONE. A single brand-blue ring vanishes on the brand-blue
   button and on the dark hero. An offset ring in the page ground with a brand
   ring outside it is visible on every surface this site has, light or dark. */
.ds-app a:focus-visible,
.ds-app button:focus-visible,
.ds-app input:focus-visible,
.ds-app select:focus-visible,
.ds-app textarea:focus-visible,
.ds-app [tabindex]:focus-visible {
  outline: 2px solid rgba(var(--p1), 1) !important;
  outline-offset: 2px !important;
  box-shadow: 0 0 0 2px rgba(var(--p5), 1) !important;
  border-radius: 6px;
}
/* controls that are already round keep their shape under the ring */
.ds-app .rounded-circle:focus-visible,
.ds-app .ds-account__btn:focus-visible,
.ds-app .ds-lang-btn:focus-visible,
.ds-app .dark_btn:focus-visible { border-radius: 50% !important; }
.ds-app .rounded-pill:focus-visible,
.ds-app .btn:focus-visible,
.ds-app .btn2:focus-visible,
.ds-app .price_btn:focus-visible { border-radius: 999px !important; }

.ds-app a:focus:not(:focus-visible),
.ds-app button:focus:not(:focus-visible),
.ds-app input:focus:not(:focus-visible),
.ds-app select:focus:not(:focus-visible) { outline: none !important; }

/* ⛔ AND A WAY PAST THE NAVIGATION. Twenty-three header links before the first
   word of the page is a long way to tab; the skip link is the one control a
   keyboard visitor needs first, so it is the first thing in the tab order and
   invisible until it is focused. */
/* ⛔ HIDDEN BY SIZE, NOT BY A NEGATIVE OFFSET.
   `top: -64px` does not remove the link, it PARKS it 64px above the page — and
   `.ds-app`, its containing block, computes `overflow-y: visible`, so nothing
   clips it. On a Mac the rubber-band overscroll at the top of the page drags the
   content down and the parked link rides into view: the owner saw a stray "Skip
   to content" button every time they flicked upward past the top.

   The standard clip is immune to it, because the link has no area to reveal —
   1x1 and clipped away until it takes focus, which is the only moment it is
   meant to exist. Never `display:none` and never `visibility:hidden`: both take
   it out of the tab order, and being first in the tab order is its whole job. */
.ds-app .ds-skip {
  /* ⛔ 1000, BECAUSE THE HEADER IS 999. The skip link lives OUTSIDE the header
     and both compete in the root stacking context, so at 200 the logo drew
     straight over the focused pill and "Skip to content" read through the
     DevSegment wordmark. One above the thing it has to cover, not a magic
     number: the header's own z-index is the number to beat. */
  /* ⛔ `fixed`, NOT `absolute`. Absolute pins it to the top of the DOCUMENT, so
     reaching it with shift+Tab from the logo half way down a 5,900px page made
     the browser scroll the whole way back up to show it. Fixed puts it at the
     top of the VIEWPORT, which is where a skip link belongs and where it was
     always drawn anyway. */
  position: fixed; left: 12px; top: 12px; z-index: 1000;
  width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%); clip: rect(0 0 0 0);
  border-radius: 10px;
  background: rgba(var(--p1), 1); color: #fff;
  font-size: 14px; font-weight: 600;
}
.ds-app .ds-skip:focus {
  width: auto; height: auto; margin: 0; padding: 10px 18px;
  overflow: visible; clip-path: none; clip: auto; color: #fff;
}

/* -- 42 ------------------------------------------------------------------
   THE LEFT RAIL GETS ITS OWN GROUND.

   `.sidebar-left` is the column beside the hero — the logo, FOLLOW US, the
   social circles and the scroll cue. It was a flat wash, so at 2056px it read as
   dead space holding four icons, next to a band that has a grid, a screenshot
   and a drawing in it.

   It takes the site's own motif at a TIGHTER pitch: the hero's grid is 96px, so
   a 32px cell here reads as the same idea at a different scale — related, not
   repeated — and the gradient runs along the column, which is the direction the
   eye travels. Both are palette tokens, so the two themes are one rule.

   ⛔ The grid sits UNDER the icons, never over them: `::before` with
   `pointer-events: none` and the rail's own children lifted to `z-index: 1`. */
.ds-app .banner-section .sidebar-left {
  position: relative;
  background-image:
    linear-gradient(180deg, rgba(var(--p1), .10) 0%, rgba(var(--p1), 0) 42%,
                            rgba(var(--p2), .07) 100%),
    linear-gradient(180deg, rgba(var(--p7), .55) 0%, rgba(var(--p7), .95) 100%) !important;
  border-right: 1px solid rgba(var(--p4), .12);
}
.ds-app .banner-section .sidebar-left::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    repeating-linear-gradient(90deg, rgba(var(--p8), .06) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(0deg,  rgba(var(--p8), .06) 0 1px, transparent 1px 32px);
  /* ⛔ THE FADE NO LONGER ENDS AT THIS ELEMENT'S BOTTOM, because the slab does
     not end there any more — it carries on through the diagonal band below (see
     `.svg-shape::before`). Fading to ZERO here put a horizontal line across the
     column exactly where the two halves meet. It hands over at .45 instead, and
     the band's copy of the grid takes it from .45 down to nothing. */
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 70%, rgba(0,0,0,.45) 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 70%, rgba(0,0,0,.45) 100%);
}
.ds-app.dark .banner-section .sidebar-left::before {
  background-image:
    repeating-linear-gradient(90deg, rgba(var(--p6), .07) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(0deg,  rgba(var(--p6), .07) 0 1px, transparent 1px 32px);
}
.ds-app .banner-section .sidebar-left > * { position: relative; z-index: 1; }

/* ⛔ AND THE PANEL HAS TO BE THE COLUMN'S SIZE, WHICH THE TEMPLATE NEVER MEANT IT
   TO BE. Their own rule is `.banner-section.index-one .sidebar-left { height:
   124% }` over a translucent grey slab — 24% of overshoot nobody could see while
   it was `rgba(131,131,131,.2)`. Painted, it is a hard-edged rectangle 737px
   tall inside a 594px column, bleeding 74px above the logo and ending in a
   horizontal cut across the hero's diagonal. Measured, not guessed.

   ⛔ `.social_info` computes `justify-content: flex-end` even though the markup
   carries Bootstrap's `justify-content-center` — a template rule outranks the
   utility class. Stated here explicitly, and scoped to the hero rail so the
   FOOTER's `.social_info`, which is a different thing with the same class name,
   is untouched. */
/* ⛔ THE PANEL IS PINNED TO THE HERO BAND, NOT LEFT TO THE TEMPLATE'S ARITHMETIC.
   Measured live at 2056px: the hero runs 0→798, its row 0→674 with `padding-top:
   80px`, the column therefore 80→674, and the diagonal `.svg-shape` that closes
   the band 674→798. The template's `height: 124%` plus a transform of its own put
   the slab at 21→615 — overlapping the logo at the top and stopping 59px short of
   the diagonal at the bottom, which is the white gap under the rail.

   Nobody could see any of that while the slab was `rgba(131,131,131,.2)`. Painted,
   every edge is a decision, so the edges are stated: `-80px` lifts the top to the
   page's own top, `100% + 80px` carries the bottom exactly to the diagonal's
   start, and the transform is cleared so nothing shifts it afterwards. */
.ds-app .banner-section.index-one .sidebar-left {
  height: calc(100% + 80px) !important;
  margin-top: -80px !important;
  transform: none !important;
}
.ds-app .banner-section.index-one .sidebar-left .social_info {
  justify-content: center !important;
}

/* ⛔ AND IT RUNS TO THE BOTTOM OF THE BANNER, NOT TO THE BOTTOM OF THE ROW.
   The rail lives in the hero's `.row`, which ends where the diagonal `.svg-shape`
   begins — measured 647 of a 771px hero. So the column's ground stopped 124px
   short and the bottom left of the banner was a plain rectangle under a panel
   with a grid in it. The owner's word for it, and he is right: it has to go down
   до упора.

   ⛔ THE HEIGHT OF THAT BAND IS SET BY ITS CONTENTS — the slide counter and the
   marquee — so there is no number to hardcode and no `100%` that means it. The
   band itself is the element that knows: this paints INSIDE it, top to bottom.

   ⛔ THE WIDTH IS DERIVED, NOT MEASURED. `col-xxl-2` is a sixth of a row that is
   24px wider than the section (Bootstrap's ±12px gutter), and the rail sits
   inside the column's own 12px padding — so the ground is
   `(100% + 24px)/6 - 24px`. Checked against four live measurements before it was
   written: 2056→323, 1728→268, 1512→232, 1440→220. Exact at all four.

   ⛔ `z-index: 0` puts it over the shape's own `<svg>`, which the template parks
   at `z-n1`, and the counter and the chip marquee both start past x=1000, so
   nothing of theirs is underneath it. */
@media (min-width: 1400px) {
  .ds-app .banner-section.index-one .svg-shape::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0;
    width: calc((100% + 24px) / 6 - 24px);
    z-index: 0; pointer-events: none;
    background-image:
      linear-gradient(180deg, rgba(var(--p2), .07) 0%, rgba(var(--p2), .07) 100%),
      linear-gradient(180deg, rgba(var(--p7), .95) 0%, rgba(var(--p7), .95) 100%);
    border-right: 1px solid rgba(var(--p4), .12);
  }
  /* the cells carry on and die out at the very bottom, which is what makes the
     two halves read as one slab rather than as a panel with a plinth. */
  .ds-app .banner-section.index-one .svg-shape::after {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0;
    width: calc((100% + 24px) / 6 - 24px);
    z-index: 0; pointer-events: none;
    background-image:
      repeating-linear-gradient(90deg, rgba(var(--p8), .06) 0 1px, transparent 1px 32px),
      repeating-linear-gradient(0deg,  rgba(var(--p8), .06) 0 1px, transparent 1px 32px);
    -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,.45) 0%, transparent 100%);
            mask-image: linear-gradient(180deg, rgba(0,0,0,.45) 0%, transparent 100%);
  }
  .ds-app.dark .banner-section.index-one .svg-shape::after {
    background-image:
      repeating-linear-gradient(90deg, rgba(var(--p6), .07) 0 1px, transparent 1px 32px),
      repeating-linear-gradient(0deg,  rgba(var(--p6), .07) 0 1px, transparent 1px 32px);
  }
}

/* -- 43 ------------------------------------------------------------------
   THE INNER PAGE HEADER. The owner's word for it was "empty", and measured it
   was: a 527px band, transparent, with the copy in the left half, nothing in the
   right, and its screenshot veiled to .99/.86 — present in the markup, invisible
   on screen. Every other surface on this site says something; this one said
   nothing on thirty-nine pages.

   It becomes a QUIETER SIBLING of the home hero, not a copy of it — a page
   header has to yield to the page:
     · the same grid at the same 96px pitch, so every page reads as one site,
       masked to fade downward so it never competes with the first section;
     · a directional field in BLUE, which is the structural accent;
     · and a breath of GREEN low and to the right. The owner asked for green, and
       green is this site's MARK colour — the stat numerals, the ticks, the
       eyebrows — so as a ground it may only ever be a whisper (§37's warm token,
       .045 light / .13 dark). Any more and it starts cancelling the marks.
     · the page's own screenshot, finally visible, filling the right half with
       something true rather than with decoration.

   ⛔ The scrim stays heavier at the BOTTOM than the hero's, because an inner
   banner's copy sits low and wide rather than in a left column. */
/* ⛔ NO `--ds-banner-*` VEIL TOKENS ANY MORE. They existed to tame a screenshot
   that is no longer in this band; see the block comment over `innerBanner()` in
   site-src/page/05-art.js for why the header is composed rather than photographed.
   These tokens are the GROUND's, and each one is a per-theme weight because the
   same alpha that reads as a whisper on a near-black skin is invisible on a white
   one — measured, not guessed. */
:root, .ds-app {
  --ds-bnr-cool:  .26;   /* blue bloom, top left — the structural accent          */
  --ds-bnr-warm:  .15;   /* green breath, low right — the MARK colour             */
  --ds-bnr-stage: .17;   /* the halo the drawing stands in                        */
  --ds-bnr-beam:  .15;
  --ds-bnr-grid:  .070;
  --ds-bnr-ink:   var(--p8);
}
.ds-app.dark {
  --ds-bnr-cool:  .30;
  --ds-bnr-warm:  .17;
  --ds-bnr-stage: .18;
  --ds-bnr-beam:  .17;
  --ds-bnr-grid:  .070;
  --ds-bnr-ink:   var(--p6);
}

/* ⛔ `align-items: center`, and it OVERRIDES an earlier `flex-end` in this file by
   coming later — the band used to hang its copy off the bottom because it held one
   full-width column. It holds two now, and two columns hung off a baseline look
   like a mistake. */
.ds-app .banner-section.inner-banner {
  position: relative;
  min-height: 540px;
  display: flex;
  align-items: center;
  /* ⛔ THE HEADER IS `position: fixed` AND SITS ON TOP OF THIS BAND. Geometric
     centring therefore is not OPTICAL centring: with the top 60px covered, the
     composition read as sitting low — measured at 1280px as a 59px gap above the
     eyebrow against 136px below the buttons. Padding the band by the header's own
     height moves the centred box into the part a reader can actually see, and it
     shifts the content by HALF that (30px), which is exactly the correction. The
     min-height does not grow with it: the tallest content here is the 430px
     drawing, and 540 - 60 still clears it. */
  padding-top: 60px;
  background-color: rgba(var(--p5), 1);
  background-image:
    radial-gradient(82% 136% at  2% -16%, rgba(var(--p1), var(--ds-bnr-cool))  0%, rgba(var(--p1), 0) 64%),
    radial-gradient(78% 124% at 98% 118%, rgba(var(--p2), var(--ds-bnr-warm))  0%, rgba(var(--p2), 0) 62%),
    radial-gradient(46%  72% at 74%  44%, rgba(var(--p1), var(--ds-bnr-stage)) 0%, rgba(var(--p1), 0) 74%),
    /* ⛔ THE WASH RUNS p5 -> p7, AND THE DIRECTION IS THE WHOLE POINT. It ran the
       other way and the band ended at pure `p5` exactly where the next section
       starts at `bg7-color` — a hard step DOWNWARD in light and upward in dark,
       measured on /products/erp as 251,252,254 meeting 233,239,247. `p7` IS that
       next section's colour, by definition, so ending on it makes the seam
       disappear in both skins with no number to keep in sync. It also puts the
       lightest part of the band under the display type, where the contrast is
       needed, instead of under the buttons. */
    linear-gradient(180deg, rgba(var(--p5), 1) 0%, rgba(var(--p5), 1) 26%, rgba(var(--p7), 1) 100%);
  background-repeat: no-repeat;
  background-size: cover;
}
@media (max-width: 991px) { .ds-app .banner-section.inner-banner { min-height: 340px; padding-top: 56px; } }

/* the same 96px pitch as every other motif on the site, masked RADIALLY from the
   headline's corner so it is densest where the copy starts and gone before it can
   touch the section below. */
.ds-app .banner-section.inner-banner::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    repeating-linear-gradient(90deg, rgba(var(--ds-bnr-ink), var(--ds-bnr-grid)) 0 1px, transparent 1px 96px),
    repeating-linear-gradient(0deg,  rgba(var(--ds-bnr-ink), var(--ds-bnr-grid)) 0 1px, transparent 1px 96px);
  /* ⛔ the first mask was a 124% radial pinned to 8% 0% and it stopped dead around
     x=560: the grid read as a stray artefact in one corner rather than as the
     band's texture. It is wider and higher now, and it is the BOTTOM RIGHT that
     it clears — which is exactly where the green bloom and the drawing are. */
  -webkit-mask-image: radial-gradient(150% 150% at 14% -14%, #000 0%, #000 44%, transparent 88%);
          mask-image: radial-gradient(150% 150% at 14% -14%, #000 0%, #000 44%, transparent 88%);
}

/* ⛔ ONE ambient motion in this band, not three. The home hero runs a mesh canvas
   AND a carousel AND four drawings, and it can: it is the show. A header that
   repeats on forty pages gets a single slow beam, 19s, translate only — no filter
   animation, no repaint, and it stops dead under `prefers-reduced-motion`. */
.ds-app .ds-bnr-beam {
  position: absolute; top: -40%; bottom: -40%; left: -12%; right: -12%;
  z-index: 0; pointer-events: none;
  background: linear-gradient(102deg,
      rgba(var(--p1), 0) 36%,
      rgba(var(--p1), var(--ds-bnr-beam)) 50%,
      rgba(var(--p1), 0) 64%);
  filter: blur(30px);
  will-change: transform;
  animation: ds-bnr-sweep 19s ease-in-out infinite alternate;
}
@keyframes ds-bnr-sweep { from { transform: translate3d(-7%, 0, 0); } to { transform: translate3d(7%, 0, 0); } }

.ds-app .banner-section.inner-banner > .container { width: 100%; position: relative; z-index: 2; }
.ds-app .ds-bnr-col { position: relative; z-index: 2; }
.ds-app .ds-bnr-art { will-change: transform; animation: ds-bnr-float 14s ease-in-out infinite alternate; }
@keyframes ds-bnr-float { from { transform: translate3d(0, -8px, 0); } to { transform: translate3d(0, 8px, 0); } }

/* at the widest breakpoint the drawing leans past the container's right edge, so
   the band reads as a composition running off the page rather than as two boxes. */
@media (min-width: 1400px) {
  .ds-app .ds-bnr-art { margin-right: -6%; }
}
@media (prefers-reduced-motion: reduce) {
  .ds-app .ds-bnr-beam, .ds-app .ds-bnr-art { animation: none; }
}

/* -- 44 ------------------------------------------------------------------
   ⛔ A 16:9 SCREENSHOT IN A PORTRAIT BOX IS NOT CROPPED, IT IS DESTROYED.
   `.choose_us` is `object-fit: cover` with its height driven by the column
   beside it. On /products that column was 620px tall and 521 wide, so a
   1600x900 screenshot — every screenshot on this site is exactly 1600x900, all
   eighteen of them — lost about 62% of its width and the BPMN diagram in it was
   cut through the middle of an element. It read as a broken image, which is
   the opposite of what a product screenshot is there to do.

   Fixing it is one line, because the source material is uniform: give the box
   the picture's own ratio and `cover` has nothing left to crop. The image then
   sits shorter than the copy beside it, which the row already centres. */
/* ⛔ `.choose_us` IS NOT REPEATED HERE. §27 owns it, and it has to: that block
   already carries the `max-width: 100% !important` that keeps the picture inside
   its column, so a second rule on the same element in a second place is two
   things that must agree and eventually will not. Only the card thumbnail, which
   §27 never touched, is set here. */
.ds-app .thump-img-area img {
  aspect-ratio: 16 / 9;
  height: auto;
  object-fit: cover;
}

/* -- 45 ------------------------------------------------------------------
   THE LIGHT THEME GETS A GROUND TO STAND ON.

   The owner's read was "тёмная выглядит круто, белая не так", and the reason is
   measurable rather than a matter of taste. The two palettes are not built the
   same way:

     dark   page 5,8,14  ->  tinted 10,17,32  ->  card 22,36,58
     light  page 251,252,254 -> card 255,255,255

   In the dark theme every step roughly DOUBLES the luminance of the one below,
   so a card is unmistakably a plane sitting above a ground. In the light theme
   the page was 251 and a card was 255 — measured live as luminance 0.97 against
   1.00. A surface and its background were the same colour, and the only thing
   separating them was a 7% hairline and a 5% shadow. That is why it read flat:
   there was nowhere for white to be.

   ⛔ THE FIX IS NOT MORE SHADOW. Piling elevation onto a white card on a white
   page is the compromise that makes a site look muddy. White has to become a
   SURFACE, which means the page has to step down and make room for it. So the
   ladder is rebuilt to the same shape the dark theme already has:

     page 240,244,250  ->  tinted 227,234,245  ->  card 255,255,255

   Every existing rule inherits it, because these are the palette's own tokens —
   nothing downstream had to be rewritten to get depth. `--p6` is untouched: it
   is the surface white AND the on-dark text colour, and it must stay 255.

   ⛔ Scoped `:not(.dark)`, never `:root`, so the dark theme — which the owner
   likes and which is correct — cannot be touched by any of this. */
/* the top of the ladder, named once so a rule can ask for it without having to
   know which skin it is in. Dark keeps the near-black card it always had. */
:root, .ds-app  { --ds-surface: var(--p5); }
.ds-app.dark    { --ds-surface: var(--p5); }

.ds-app:not(.dark) {
  --ds-surface: var(--p6);   /* light: white is the surface, not the floor */
  --p5:  240, 244, 250;   /* the page ground: white is no longer the floor */
  --p7:  227, 234, 245;   /* the deeper band, still clearly below the page   */
  --p10: 250, 252, 255;   /* a raised surface, was level with the old page   */
  --b1:  222, 231, 243;   /* borders follow the ground down                  */
}

/* ⛔ AND THE PAGE ITSELF HAS TO SIT ON THE NEW FLOOR, which moving a token does
   NOT do on its own. `--p5` is a colour other rules reach for; nothing paints the
   body with it. Measured after the token change: `.ds-app` was still
   `rgb(255,255,255)` at luminance 1.00, so an untinted section and a white card
   on it were once again the same colour and the depth only appeared on the bands
   that happened to be tinted. Said explicitly, once.

   ⛔ ELEVATION IS NOT REPEATED HERE. §23 already lifts `.single-item`,
   `.counter_right_side`, `.pricing-section .single-box`, `.contact-form-box` and
   `.ds-panel`, and §38 hand-excludes `.header-section .single-item` because the
   template reuses that class for the 44px apps-button wrapper — the white square
   the owner had to point at twice. A second elevation block would have to repeat
   that exclusion to stay correct, and two places that must agree eventually do
   not. §23 keeps it. */
.ds-app:not(.dark) { background-color: rgba(var(--p5), 1); }

/* the hero chips were a translucent p7 pill on a p5 page — two greys 18 levels
   apart. On the new ground they are a white surface like every other card. */
.ds-app:not(.dark) .banner-section.index-one .marquee-section .item-area {
  background-color: rgba(var(--p6), 1) !important;
  border-color: rgba(var(--p8), .10) !important;
  box-shadow: 0 1px 2px rgba(var(--p8), .05), 0 8px 20px -14px rgba(var(--p8), .18);
}

/* -- 46 ------------------------------------------------------------------
   THE BUTTONS, SETTLED.

   ⛔ THE SAME BUTTON LOOKED LIKE TWO DIFFERENT PRODUCTS, 500px APART. Measured
   live on the home page: the header's "Request a demo" was a 999px pill with a
   coloured glow, the hero's "Request a demo" was an 8px rectangle with no shadow
   at all — while the chips beside it are pills at 800px and the cards are 16px.
   §21 already asks for a pill and the header obeys it; something later in the
   cascade was winning on the hero, and hunting it is not worth another hour when
   the correct value can simply be stated and verified. It is stated here, last,
   and measured after.

   ⛔ AND THE PRIMARY'S FILL FADED TO 84% AT THE BOTTOM. Over a dark ground that
   reads as a smudge under the label, over a light one as a washed-out edge;
   either way it is a gradient nobody asked for doing the work of a shadow. A
   solid brand fill with a coloured lift beneath it is the same idea done with
   the property that means it.

   ⛔ THE SECONDARY IS A SURFACE, PER SKIN. It was `rgba(--p8, .07)` — which on
   the dark skin is 7% of a LIGHT ink and on the light skin 7% of a dark one, so
   one declaration meant two different things and neither was a button. Light
   gets the same white surface as a card, dark gets a light film. */
.ds-app .btn,
.ds-app .btn2,
.ds-app .price_btn {
  border-radius: 999px !important;
}
.ds-app .btn,
.ds-app .btn2,
.ds-app .price_btn {
  background: rgba(var(--p1), 1) !important;
  background-image: none !important;
  border: 1px solid rgba(var(--p1), 1) !important;
  color: #fff !important;
  box-shadow:
    0 1px 2px rgba(var(--p1), .32),
    0 10px 24px -10px rgba(var(--p1), .70) !important;
}
.ds-app .btn:hover,
.ds-app .btn2:hover,
.ds-app .price_btn:hover {
  background: rgba(var(--p1), .92) !important;
  transform: translateY(-2px);
  box-shadow:
    0 2px 4px rgba(var(--p1), .34),
    0 18px 36px -12px rgba(var(--p1), .85) !important;
}
.ds-app .btn:active,
.ds-app .btn2:active,
.ds-app .price_btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px -4px rgba(var(--p1), .70) !important;
}

/* ⛔ AND THE LABEL IS A SPAN, WHICH IS WHERE THE COLOUR ACTUALLY LANDS. Setting
   `color: #fff` on the button does nothing for `.btn-text-one` / `.btn-text-two`
   — the template's two sliding labels — because something further down colours
   them directly. Measured on the light skin: the span computed `rgb(8,21,37)`
   over the blue fill, a ratio of 3.15 where 15px text needs 4.5. The button read
   as dark-on-blue and that is exactly what made it look cheap. */
.ds-app .btn:not(.second) .btn-text-one,
.ds-app .btn:not(.second) .btn-text-two,
.ds-app .btn2 .btn-text-one,
.ds-app .btn2 .btn-text-two,
.ds-app .price_btn .btn-text-one,
.ds-app .price_btn .btn-text-two,
.ds-app .btn:not(.second) span,
.ds-app .btn2 span,
.ds-app .price_btn span { color: #fff !important; }

/* --- secondary ---------------------------------------------------------- */
.ds-app:not(.dark) .btn.second {
  background: rgba(var(--p6), 1) !important;
  border: 1px solid rgba(var(--p8), .14) !important;
  box-shadow:
    0 1px 2px rgba(var(--p8), .05),
    0 8px 22px -14px rgba(var(--p8), .22) !important;
}
.ds-app:not(.dark) .btn.second:hover {
  border-color: rgba(var(--p1), .55) !important;
  box-shadow:
    0 2px 4px rgba(var(--p8), .07),
    0 16px 32px -16px rgba(var(--p8), .30) !important;
}
.ds-app.dark .btn.second {
  background: rgba(var(--p6), .08) !important;
  border: 1px solid rgba(var(--p6), .22) !important;
  box-shadow: inset 0 1px 0 rgba(var(--p6), .10) !important;
}
.ds-app.dark .btn.second:hover {
  background: rgba(var(--p6), .14) !important;
  border-color: rgba(var(--p1), .80) !important;
  box-shadow: 0 14px 30px -16px rgba(0, 0, 0, .8) !important;
}
/* the label belongs to the button it sits in, in both skins */
.ds-app .btn.second,
.ds-app .btn.second .btn-text-one,
.ds-app .btn.second .btn-text-two { color: rgba(var(--p8), 1) !important; }

/* the FAQ question: ink, not accent — see the note in R.faq */
.ds-app .faq-section .ds-faq__q { color: rgba(var(--p8), .96) !important; }
.ds-app .faq-section .faq:hover .ds-faq__q { color: rgba(var(--p1), 1) !important; }
.ds-app.dark .faq-section .faq:hover .ds-faq__q { color: rgba(var(--p3), 1) !important; }

/* -- 47 ------------------------------------------------------------------
   reCAPTCHA v3's badge, and the sentence that pays for hiding it.

   ⛔ THE BADGE IS A FLOATING BUBBLE IN THE BOTTOM-RIGHT CORNER OF EVERY PAGE the
   script runs on — 70x60, `position: fixed`, `z-index: 2000000000`, in Google's
   own styling. It sits over the content, it is the only element on the site that
   belongs to another brand, and on a phone it lands on top of the last line of
   the footer. Google permits hiding it only if the page carries the disclosure
   with both links; `captchaNote()` in 20-forms.js writes it under every form,
   and the two must never be separated.

   ⛔ `visibility`, not `display: none`. The widget still has to run and be
   measurable; hiding it by display has been reported to break execution in some
   versions, and there is nothing to gain from finding out. */
/* ⛔ UNSCOPED, because the badge is not ours to place. Google appends it to
   whatever it likes — measured on /demo as still `visible` while the rule was
   written as `.ds-app .grecaptcha-badge, body > .grecaptcha-badge`, neither of
   which matched. The class is the only thing about it we can rely on. */
.grecaptcha-badge { visibility: hidden !important; }

.ds-app .ds-captcha-note { opacity: .72; line-height: 1.5; }
.ds-app .ds-captcha-note a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.ds-app .ds-captcha-note a:hover { color: rgba(var(--p1), 1); }

/* -- 48 ------------------------------------------------------------------
   THE PHONE HEADER. Reported from an iPhone as "валится всё друг на друге",
   and it was three faults stacked, measured in a 390px iframe:

   (a) THE RIGHT-AREA WAS PINNED OUT OF THE FLOW. Their own mobile rule takes
       `.custom-pos` `position: absolute; right: 20%`, which is sound for the
       ONE button the template shipped there. Ours carries five controls —
       language, theme, the panel opener, the account circle and the CTA — 389px
       of them. Pinned by its RIGHT edge at 20% of a 390px screen, its left edge
       landed at −82px: the row began off the left of the screen and every
       control sat on top of the logo. An absolute box cannot make room for
       itself, so on phones it goes back into the flow and `margin-left: auto`
       does the pushing.

   (b) TWO MENUS, BOTH REACHABLE, BOTH OPEN AT ONCE. The template has a burger
       (`.navbar-toggler`, a Bootstrap collapse of the desktop nav) AND a
       nine-dots button opening a full-height side panel. Measured: pressing one
       then the other left BOTH on screen, the collapse's white labels over the
       panel's list — which is literally what was reported. The collapse also
       draws on nothing: it has no ground of its own, so the hero read straight
       through the menu. The side panel wins on every count — it is opaque, it
       is full height, and it carries the grouped sitemap plus the contacts
       rather than five top-level words — so below `lg` the burger goes and the
       panel is the only door. Its icon becomes a hamburger at the same width
       (main.html), because nine dots is not what a thumb is looking for.

   (c) NOTHING LEFT ROOM FOR ANYTHING. Four 44px circles, 16px gaps and a 163px
       logo need 403px of a 390px screen. The circles step down to 40px and the
       gap to 8px here, and the logo takes a ceiling — 337px used of 374px
       available at 390px, and it still fits a 360px Android.

   ⛔ The CTA is NOT simply dropped. Restoring `d-none` (see the button block
   above) correctly takes the pill out of the bar below 1200px, but that would
   leave a phone with no demo button at all in the chrome; it is added to the
   side panel instead, full width, where it is the last thing under the menu. */
@media (max-width: 991px) {
  /* ⛔ THE BAND MUST BELONG TO THE THEME. Below 992px their stylesheet paints
     the header `rgba(var(--f1), 1)` — and `--f1` is a FIXED token, the same
     16,27,44 in both skins, because the template only ever had a dark one. The
     glyphs are `--p8`, which does follow the skin. In the light theme that put
     near-black icons (8,21,37) on that fixed dark bar: measured contrast 1.06
     against the 16.63 the same icons get on the desktop header, i.e. a row of
     controls that is simply not there for a visitor on a phone.
     `--p7` is the one-step-from-the-ground tone and it exists in both skins
     (227,234,245 light / 10,17,32 dark), so the bar keeps the raised look the
     template wanted and the icons keep their contrast in either. */
  .ds-app .header-section { background: rgba(var(--p7), 1); }

  .ds-app .header-section .navbar .custom-pos {
    position: static !important;
    right: auto !important; top: auto !important;
    margin-left: auto;
    gap: 8px !important;
    flex-wrap: nowrap;
  }
  /* one door, not two — see (b) */
  .ds-app .header-section .navbar .navbar-toggler { display: none !important; }
  .ds-app .header-section .navbar .navbar-collapse { display: none !important; }

  /* ⛔ the row may never be the reason the page scrolls sideways */
  .ds-app .header-section .navbar { flex-wrap: nowrap; min-width: 0; }
  .ds-app .header-section .navbar .navbar-brand { flex: 0 1 auto; min-width: 0; }

  .ds-app .header-section .navbar .custom-pos .dark_btn,
  .ds-app .header-section .navbar .custom-pos .ds-lang-btn,
  .ds-app .header-section .navbar .custom-pos .ds-account__btn {
    width: 40px; height: 40px; min-width: 40px; flex: 0 0 auto;
  }
  /* ⛔ `img.logo`, NOT `.logo` — ONE ELEMENT SELECTOR DECIDED IT. Their own
     mobile rule is `.ds-app .header-section .navbar .navbar-brand img
     { max-width: initial }`, which is (0,4,1); written as `.ds-app
     .header-section .navbar-brand .logo` this was (0,4,0) and lost in silence.
     Measured at 360px: the logo stayed 163px wide and pushed the menu button
     half off the right edge. Matching their shape and adding the class wins on
     the same ground. */
  .ds-app .header-section .navbar .navbar-brand img.logo { max-width: 148px; height: auto; }

  /* ⛔ THE DOOR GOES LAST, AT THE EDGE. Left where the template had it, the menu
     opener sat third of four, with the account circle outboard of it — the one
     control every visitor uses, placed where the thumb has to reach past a
     control most visitors never see. It takes the right edge on phones. */
  .ds-app .header-section .navbar .custom-pos .single-item.target-item { order: 9; }

  /* the bar's pill is simply not in the bar here — see `.ds-cta-bar` below */

  /* the panel's own button, full width, first thing under the brand */
  .ds-app .ds-drawer-cta { margin-top: 4px; }
  .ds-app .ds-drawer-cta .btn2 { width: 100%; min-width: 0; }
}
/* ⛔ 380, NOT 400. At 390 — the iPhone this was reported from — the row needs
   356px of 390 with the 148px mark, so the smaller logo would have been a loss
   for nothing. The step belongs where the room actually runs out: a 360px
   screen, where 148px leaves the menu button 11px over the edge. */
/* ⛔ THE BAR'S CTA CARRIES ITS OWN BREAKPOINT, NOT `d-none d-xl-block`.
   Bootstrap's display utilities are `!important` by construction, and on a
   button `display` is not visibility — it is the layout that stacks the two
   sliding labels in one grid cell (§46). Whichever of the two is written with
   `!important` wins and the other breaks: with `!important` on §46 the pill
   would not hide on a phone; without it, `d-xl-block` made the pill
   `display: block` and the label sat at the TOP of the pill, measured on
   devsegment.com at y=8..29 inside a 46px button with the second copy clipped.
   Neither side states `display` with `!important` any more, and the two jobs sit
   on two different rules, so there is nothing left to win. */
.ds-app .ds-cta-bar { display: none; }
@media (min-width: 1200px) {
  .ds-app .ds-cta-bar { display: inline-grid; }
}

@media (max-width: 380px) {
  .ds-app .header-section .navbar .navbar-brand img.logo { max-width: 128px; }
}

/* ⛔ AND ONE MORE STEP FOR 320. A VISITOR never sees this: the account circle is
   drawn only for someone signed in, so a phone that small carries three controls
   and has room. An AUTHOR on a 320px screen carries four, and the measurement at
   that width put the menu button 24px past the edge — clipped, not scrollable,
   because the bar is inside an overflow-hidden band. 36px circles and a 6px gap
   bring the row to 314px of 320. */
@media (max-width: 340px) {
  .ds-app .header-section .navbar .custom-pos { gap: 6px !important; }
  .ds-app .header-section .navbar .custom-pos .dark_btn,
  .ds-app .header-section .navbar .custom-pos .ds-lang-btn,
  .ds-app .header-section .navbar .custom-pos .ds-account__btn {
    width: 36px; height: 36px; min-width: 36px;
  }
  .ds-app .header-section .navbar .navbar-brand img.logo { max-width: 112px; }
}

/* -- 29 ------------------------------------------------------------------
   ⛔ ORDER, NOT SPECIFICITY.
   `.slide-img` was hidden below 992px in a media query near the top of this
   file, and then §17(c) set `display: flex` on the same selector further down
   to fix its width. A media query adds NO specificity — the later rule simply
   won, and the hero diagram stayed on screen at 390px where it has no room,
   pushing the headline off the fold. Verified in a 390px iframe, because
   resizing the window does not change this page's CSS viewport.

   Anything that must beat a rule stated later has to BE later. This block is
   last in the file for that reason; keep new rules above it. */
@media (max-width: 991px) {
  .ds-app .banner-section .slide-img { display: none !important; }
}
