/* =====================================================================================================
   DDC RULES LAYER - slot 2 (30 Sept 2026 layout pass). Hand-owned: NOT generated, never part of mock.css.
   Richard: "our rules for these must always translate even into Elementor builds."

   The kit's rail, card, rhythm, spacing, type and control rules, made to hold on ANY Elementor V4 output:
   mock-parity pages, interim pages and Theme Builder templates alike. Tokens only; no Elementor hash
   classes, no data-ids, no !important, no rule that depends on how many items a client publishes.
   Enqueued LAST (after mock.css). Three parts, by how long each rule should live:
   1. KIT RULES (top): kit classes only - they hold whatever the builder does.
   2. ELEMENTOR ADAPTER: rules keyed to Elementor's generated markup - re-check on every Elementor update.
   3. COMPAT (end): the interim pages only - deleted in one cut at their mock-parity rebuild.
   - Corrections use "body .elementor ..." (0,2,1) or higher: they beat Elementor's global classes and
     per-element local styles (0,2,0) and, by order, the kit rules in mock.css at the same weight.
   - Slot 1 (v4-reset.css) neutralises Elementor's own defaults; this file never repeats that.
   - Media queries are the kit's breakpoints (1024 / 900 / 640 / 480) in range syntax, so a fractional
     viewport (browser zoom, display scaling) never falls between two rules. Custom properties cannot be
     used inside a media query, so those are the only literal pixel values here.
   Measured before shipping on 12 page types at 1440 / 1024 / 768 / 390 (verification/2026-09-30/
   layout-pass), challenged, and reviewed by the Moirai. Re-measure with site/tools/measure/measure.mjs.
   ===================================================================================================== */


/* --- R1 RAIL: content spans the card, gutter to gutter ---------------------------------------------- */

/* The kit centres .shell with margin-inline:auto; as a flex item (every Theme Builder section is an
   e-flexbox) that shrink-wrapped it to its content, so case study, social value and search content sat in
   the middle of the card and search results collapsed to a 92px column. max-width + auto margins still
   centre it above 1620px. */
body .elementor .shell { width: 100%; }


/* --- R4 RHYTHM: one pill gap, groups further apart than their parts --------------------------------- */

/* The kit's ".pill + *{margin-top:20}" is a block-flow rule; V4 containers are flex or grid with a gap,
   so it doubled to 40 (38 in the footer). Spacing comes from the container's gap. */
body .elementor .pill + * { margin-top: 0; }

/* A carousel followed by more content in its card takes the kit's 60 group gap (kit.css
   .carousel--projects), so the projects arrows no longer sit on THE TEAM pill. */
body .elementor .slot:has(.ddc-sc.carousel):not(:last-child) { margin-bottom: var(--gap-60); }


/* --- R6 TYPE: a measure, no empty gaps, lists that read -------------------------------------------- */

/* A lead always keeps a measure (one ran 924px wide at 1024). Zero weight: any max-width a kit class or
   a builder sets still wins. */
body :where(.elementor .lead) { max-width: var(--measure); }

/* A dynamic tag that returns nothing leaves no gap. (:empty only matches a truly empty element; a
   whitespace-only one still renders, which is harmless.) */
body .elementor :is(p, h1, h2, h3, h4, h5, h6):empty { display: none; }

/* Unclassed lists: bullets inside the text edge, items further apart than their lines. */
body .elementor :is(ul, ol):not([class]) {
  display: grid;
  gap: var(--gap-8);
  padding-inline-start: var(--gap-20);
}
/* Phones: list items are one- or two-line phrases; balance them so none ends on a single word. Phones only,
   so a hyphenated term ("OS19X-compliant") is never split at the in-between widths. */
@media (width <= 640px) {
  body .elementor :is(ul, ol):not([class]) > li { text-wrap: balance; }
}


/* --- R7 / R12 BALANCE: whole rows --------------------------------------------------------------------- */

/* Home stats in whole rows: 4, then 2 x 2, then the kit's 1 - the first stat of each row on the rail,
   dividers only between neighbours, a hairline between rows. */
@media (width > 1024px) {
  body .elementor .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (640px < width <= 1024px) {
  body .elementor .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body .elementor .stat:nth-child(odd) { padding-left: 0; border-left: 0; }
  body .elementor .stat:nth-child(n + 3) { border-top: var(--hair) solid var(--line); }
  body .elementor .stats--ink .stat:nth-child(n + 3) { border-top-color: var(--on-ink-line); }
}
/* Phones: no empty stat padding under the last figure (visual bottom = --section-y). */
@media (width <= 640px) {
  body .elementor .stat:last-child { padding-bottom: 0; }
}


/* --- R9 CONTROLS: sized to content, full width on phones --------------------------------------------- */

/* Flex and grid parents stretch their items; the kit only ever put .btn and .link in block flow or a
   .btn-row. Any non-auto width stops the stretch (Services "Talk to us" ran 1340px, the home submit
   678px, link underlines the full column). Separate selectors on purpose: an :is() list takes the weight
   of its heaviest argument and would lift this above the phone rule below. */
body .elementor .btn,
body .elementor .link { width: fit-content; }
body .elementor .contact__text a { width: fit-content; }

/* Phones: every button full width with a centred label (Richard, 17 Aug and 30 Sept 2026). Button rows
   stack through the kit's own .btn-row phone rule. */
@media (width <= 640px) {
  body .elementor .btn { width: 100%; justify-content: center; }
}


/* --- R10 HEROES AND IMAGES ------------------------------------------------------------------------------ */

/* A card whose picture is missing (Elementor prints the img with no src at all) drops its media box
   rather than showing an empty frame. An empty src is left alone: it can be a lazy image about to load. */
body .elementor .project__media:has(> img:not([src])) { display: none; }

/* A panel's own action (the essence panel's button, Richard 30 Sept) sits in the bottom-right corner, on
   the same 40px inset as the panel's text; on phones it drops below the text so it never covers it. */
body .elementor .panel__action { position: absolute; inset-inline-end: var(--gap-40); inset-block-end: var(--gap-40); z-index: 1; width: auto; --width: auto; min-width: 0; padding: 0; } /* width: Elementor's 100% default pinned the button to the LEFT (measured 1 Oct) */
@media (width <= 640px) {
  body .elementor .panel:has(> .panel__action) { flex-wrap: wrap; }
  body .elementor .panel__action { position: static; order: 2; width: 100%; padding: 0 var(--gap-40) var(--gap-40); }
}

/* Portraits crop from the bottom only, never the crown (the kit's centre crop trimmed heads). */
body .elementor .person__media img { object-position: center top; }

/* .panel and .mark both own ::after on the essence panel, so its scrim shrank to the brackets' 14px square
   and the text sat on the bare photo. The scrim wins (text over a photo must stay legible); if the
   survey-mark brackets are wanted on that panel they need their own pseudo-element. */
body .elementor .panel.mark::after { width: auto; height: auto; border: 0; }


/* =====================================================================================================
   ELEMENTOR ADAPTER - rules keyed to the markup Elementor generates (its location wrappers, loop
   wrappers, V4 base classes, background-video parts). Those names are Elementor's, not a public API:
   RE-CHECK THIS SECTION ON EVERY ELEMENTOR UPDATE by running site/tools/measure/measure.mjs (block gaps
   of 10, content on the rail, leak count 0). Kit-owned rules above hold whatever the builder does.
   ===================================================================================================== */

/* R2 CARDS - 10px between every pair of cards, the footer included. */
/* Elementor Pro's location clearfix (.elementor-location-footer::before{display:table}) stopped the last
   card's and the footer card's 10px margins collapsing: 20px above the footer on every page. V4 layouts
   have no floats, so the clearfix has no job. */
body .elementor-location-footer::before { content: none; }

/* R4 - the pill gap comes from the container, so the block-flow parents that relied on the kit's pill
   margin get the gap instead: the home intro heading column, and loop cards that hold a pill. */
body .elementor .intro > .e-div-block-base:not(.intro__body),
body .elementor .e-collection-loop-layout-base > .e-con:has(> .pill) {
  display: grid;
  gap: var(--gap-20);
  align-content: start;
}

/* A link that follows a loop grid belongs to the section, not to the last card. */
body .elementor .e-collection-loop-base + .link { margin-top: var(--gap-40); }

/* R10 - the hero's content above its scrim. */
/* Everything that is not the hero's media or its video controls paints above .hero::after. The kit lifts
   only .hero__inner, so any hero whose content sits in a plain .shell had its title, lead and buttons
   dimmed by the scrim. */
body .elementor .hero > :not(.hero__media, .e-background-video__media, .hero__controls, .e-background-video__controls, .hero-video, video, img) {
  position: relative;
  z-index: 1;
}


/* =====================================================================================================
   COMPAT - interim pages only (About, Services, Our Work, Contact, Careers, Privacy, Cookies, footer 82,
   and the Theme Builder sections built with the interim classes). DELETE THIS WHOLE SECTION when those
   are rebuilt on the mock's classes (build-home-mock / mock-ui components).
   ===================================================================================================== */

/* Interim heroes already paint the kit's --scrim inside their own Elementor background (lib.mjs
   bg(photo, SCRIM)). Now the photos show again (port-kit emits kit surfaces as background-color), the
   kit's ::after would scrim them twice. One scrim. */
body .elementor .section-card.hero::after { content: none; }

/* The builders ask for 40 between title, lead and actions (the kit's .hero__foot margin); the compat
   shell gap of 20 overrode it. */
body .elementor .section-card.hero > .shell { gap: var(--gap-40); }

/* Section heads: head group to its content 40 (shell gap 20 + 20), as the kit's .head margin-bottom.
   Home sections are block e-div-blocks and never match; the footer card is excluded. */
body .elementor :is(.section-card:not(.hero, footer), .section.e-flexbox-base) > .shell > :is(.pill, h1, h2, h3, h4, p) + .e-con:not(.link) {
  margin-top: var(--gap-20);
}
body .elementor .section-card > .shell > .e-collection-loop-base + .link { margin-top: var(--gap-20); }

/* Person cards (About, Careers): photo to name 20, name to role 10, as the kit's .person. */
body .elementor .e-collection-loop-layout-base > .e-con:has(> .media-45) {
  display: grid;
  gap: var(--gap-10);
  align-content: start;
}

/* Card grids use the kit's .cards rhythm, 40 between rows, 20 between columns. */
body .elementor .e-collection-loop-layout-base:has(> .e-con > :is(.media-43, .media-45), > .e-con > a > .media-43) {
  row-gap: var(--gap-40);
  column-gap: var(--gap-20);
}

/* Interim global classes name the font alone (var(--font-main) = "Roboto", no fallback), so a late or
   failed webfont dropped them to Times. The kit's stack. */
body .elementor :is(.h-display, .h-1, .h-2, .h-3, .h-4, .text, .stat-figure, .stat-label, .stat-note, .need-q, .need-a) {
  font-family: var(--font);
}

/* A stacked row keeps its button on the left rail, not pushed right by a desktop align-items:flex-end
   (Theme Builder CTAs on 179 / 180 / 181). */
@media (width <= 1024px) {
  body .elementor .shell-row > .btn { align-self: flex-start; }
}

/* About values in equal rows (3 + 3, 2 + 2 + 2, 1), figure at the top and title and text at the foot of
   each card, as the kit's .need; no taller than its content on phones. */
body .elementor .shell-row:has(> .need-card) {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (width <= 900px) {
  body .elementor .shell-row:has(> .need-card) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (width <= 640px) {
  body .elementor .shell-row:has(> .need-card) { grid-template-columns: minmax(0, 1fr); }
  body .elementor .need-card { min-height: 0; }
}
body .elementor .need-card > .h-4 { margin-top: auto; }

/* Phones: button rows the interim and Theme Builder builders emitted WITHOUT the kit's .btn-row stack
   too - detected as a flexbox whose children are all buttons. This reads Elementor's DOM shape, so it lives
   here and dies with the rebuild, which puts .btn-row on those rows (Moirai, 30 Sept). */
@media (width <= 640px) {
  body .elementor .e-flexbox-base:has(> .e-button-base):not(:has(> :not(.e-button-base, .btn))) {
    flex-direction: column;
    align-items: stretch;
  }
}

/* Footer part 82: column heads are the builder's plain labels (the kit .pill repainted them as chips),
   head and links 10 apart. */
body [data-elementor-type="footer"] .col { row-gap: var(--gap-10); }
body [data-elementor-type="footer"] .col > .pill { padding: 0; background-color: transparent; }
/* Footer part 82 as the mock's .foot: column links and details at body size, links white (green on hover);
   only the copyright line stays small (1 Oct, Richard: "exactly the mock"). */
body [data-elementor-type="footer"] .col > .small { font-size: var(--fs-body); }
body [data-elementor-type="footer"] .col a { color: var(--white); text-decoration: none; }
body [data-elementor-type="footer"] .col a:hover { color: var(--ddc-green); }

/* Footer part 82 at tablet in whole rows - 2 x 2 instead of one left-hand column (it was 1014px tall at
   1024) - then 1 on phones. Desktop keeps its row. */
@media (width <= 1024px) {
  body [data-elementor-type="footer"] .shell-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--gap-40);
  }
}
@media (width <= 480px) {
  body [data-elementor-type="footer"] .shell-row { grid-template-columns: minmax(0, 1fr); }
}

/* Plain heroes (no photo) always clear the floating nav, at any window height (1 Oct 2026: on a
   1440x675 screen the hero pill sat under the nav; the templates own top padding loses to v4-reset). */
body .elementor header.section > .shell { padding-top: calc(var(--nav-top) + var(--nav-h) + var(--gap-40)); }

/* Sticky side columns (1 Oct 2026): on service, article and role pages both columns stick under the nav,
   so whichever is shorter travels with the reader and the two finish together. Tall desktop windows only:
   a stuck column taller than the window would hide its own end. overflow: clip keeps the card's rounded
   clipping without making it a scroll container (hidden would switch sticky off). */
@media (min-width: 1025px) and (min-height: 800px) {
  body .elementor .section:has(.service-cta, .post-author, .role-apply) { overflow: clip; }
  body .elementor .shell-row:has(.service-cta, .post-author, .role-apply) > .col {
    position: sticky; top: calc(var(--nav-top) + var(--nav-h) + var(--gap-20)); align-self: flex-start;
  }
}

/* =====================================================================================================
   R12 - Jet's 12 Oct list: one button, bolder hero leads, green pills. Loads after mock.css, and the
   doubled class out-ranks every kit variant (mock.css 56-75), .on-ink .btn--primary included.
   ===================================================================================================== */

/* One button everywhere: purple, bold white caps, no icon. Hover and focus turn green with white text.
   White on the brand green (#1FA824) is 3.14:1, under AA for text this size, so the hover fill is the
   kit's fill-safe green (DESIGN.md: --ddc-green-fill #167A1C, 5.47:1); the brand green stays on
   highlights, icons and marks. The form submit is the same button (forms.css drew it an arrow disc). */
body .elementor .btn.btn,
body .elementor .e-form-base button[type="submit"] {
  background-color: var(--ddc-purple);
  color: var(--white);
  border-color: var(--ddc-purple);
  padding-inline: var(--gap-20);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  backdrop-filter: none;
}
body .elementor .btn.btn:is(:hover, :focus-visible),
body .elementor .e-form-base button[type="submit"]:is(:hover, :focus-visible) {
  background-color: var(--ddc-green-fill, #167A1C);
  color: var(--white);
  border-color: var(--ddc-green-fill, #167A1C);
}
body .elementor .btn .btn__icon,
body .elementor .e-form-base button[type="submit"]::after { display: none; }
/* 13 Oct 2026 (Richard's eyeball on live): the one-button rule painted the light variant purple as well, so on a
   purple panel (the role page's Apply panel, the Careers and Services CTA cards) the button read as floating
   white text with no box. The light variant keeps the caps and no icon, on the fill-safe green (white on it
   5.47:1); hover and focus flip it to white with purple text (8.6:1), still a box on purple. Same weight as the
   rule above, later in the file: order decides, no extra class. */
body .elementor .btn.btn--light {
  background-color: var(--ddc-green-fill, #167A1C);
  color: var(--white);
  border-color: var(--ddc-green-fill, #167A1C);
}
body .elementor .btn.btn--light:is(:hover, :focus-visible) {
  background-color: var(--white);
  color: var(--ddc-purple);
  border-color: var(--white);
}

/* Hero subheadings: bolder first, a box only if that is not enough (Jet). Photo heroes (.hero) and the
   plain heroes of the Theme Builder templates (header.section: services, roles, news) alike. */
body .elementor :is(.hero, header.section) :is(.lead, .hero__lead) { font-weight: 600; }

/* Pills green (Jet: "could the pills be green?"): the fill-safe green with white caps, everywhere except
   the footer's column heads, which are plain labels (footer block above). */
body .elementor .pill.pill:not([data-elementor-type="footer"] .pill),
body .elementor .ddc-sc .pill {
  background-color: var(--ddc-green-fill, #167A1C);
  color: var(--white);
  border-color: transparent;
  backdrop-filter: none;
}
