@font-face {
  font-family: anova-light;
  font-style: normal;
  font-weight: 300;
  font-display: fallback;
  src: url("/styles/fonts/anova-light.woff2") format("woff2");
}

@font-face {
  font-family: anova-reg;
  font-style: normal;
  font-weight: 400;
  font-display: fallback;
  src: url("/styles/fonts/anova-reg.woff2") format("woff2");
}

@font-face {
  font-family: anova-bold;
  font-style: normal;
  font-weight: 700;
  font-display: fallback;
  src: url("/styles/fonts/anova-bold.woff2") format("woff2");
}



:root {
  /* font tokens */
  --font-primary-1: "anova-light", Arial, Helvetica, sans-serif;
  --font-primary-2: "anova-reg", Arial, Helvetica, sans-serif;
  --font-primary-3: "anova-bold", Arial, Helvetica, sans-serif;

  /* grid */
  --grid-container-width: 83.4%;
  --grid-column-width: calc(var(--grid-container-width) / 12);
  --grid-gutter-width: 8.3%;

  /* -----------------------------------------------------------------------
     SAS design-token layer (the finalized palette from the design team's
     styles/new-styles.css). This is the ONLY color palette — the legacy
     piecemeal --color-{gray|green|teal|blue|…}-NNN scales were removed; their
     few live usages were remapped to the brand tokens below.

     There is ONE scale per concern, and blocks consume only the semantic layer:

       color        --color-primary-N / --color-secondary-N / --color-neutral-N
       type size    --text-2xs … --text-7xl (fluid clamp)
       line height  --leading-tight … --leading-relaxed
       spacing      --spacing-0 … --spacing-6xl
       radius       --border-radius-s / -m / -l / -pill / -circle
       shadow       --shadow-s / -m / -l
       border       --border-s / -m / -l (+ --border-width-s / -m / -l)

     Removed in the EDGE-362 token consolidation — do NOT reintroduce:
       - the light/dark color schemes (`color-scheme`, `.light-scheme`,
         `.dark-scheme`, `light-dark()`); the site is light-only
       - the aliasing named-color layer (--color-text, --color-link,
         --color-brand, --color-accent, --color-background, --color-shaded,
         --color-charcoal, --color-light, --color-light-base, --color-dark,
         --color-dark-base) — use the semantic --color-… tokens directly
       - the fixed --heading-font-size-… and --body-font-size-… scales, plus
         --heading-line-height / --body-line-height — use --text-… / --leading-…

     The full spacing scale is the design team's. The legacy fixed-px
     --spacing-l/xl/xxl were migrated to it: every consumer was re-pointed to the
     fluid step whose max matches the old fixed value (l 24px→xl, xl 32px→2xl,
     xxl 48px→3xl), so desktop spacing is unchanged while gaining fluid scaling.
     ----------------------------------------------------------------------- */

  /* SAS base color tokens — primary */
  --blue-light:     #c4defd;
  --blue-med:       #4398f9;
  --sas-blue:       #0766d1;
  --blue-deep:      #032954;

  /* neutral group (legacy gray equivalents noted) */
  --white:          #fff;
  --linen:          #f8f9fb; /* legacy gray10 */
  --fog:            #e9edf1; /* legacy gray9 */
  --stone:          #ccd3dd; /* legacy gray6 */
  --smoke:          #b1bac8; /* legacy gray2 */
  --sas-gray:       #7e889a; /* legacy gray */
  --charcoal:       #454e5d; /* legacy gray3 */
  --black:          #000;

  /* secondary group */
  --teal-light:     #79e8f2;
  --teal:           #3adbe6;
  --teal-deep:      #06c1cc;
  --yellow-light:   #ffdf73;
  --yellow:         #fc3;
  --yellow-deep:    #d9a30b;
  --red-light:      #f99;
  --red:            #f24949;
  --red-deep:       #cc2d2d;
  --green-light:    #79f2af;
  --green:          #36d982;
  --green-deep:     #29b869;
  --pink-light:     #ff66b2;
  --pink:           #db127d;
  --pink-deep:      #850d4a;

  /* SAS named color tokens (semantic map) */
  --color-primary-1:    var(--sas-blue);
  --color-primary-2:    var(--blue-deep);
  --color-primary-3:    var(--blue-light);
  --color-primary-4:    var(--blue-med);
  --color-secondary-1:  var(--teal-light);
  --color-secondary-2:  var(--teal);
  --color-secondary-3:  var(--teal-deep);
  --color-secondary-4:  var(--yellow-light);
  --color-secondary-5:  var(--yellow);
  --color-secondary-6:  var(--yellow-deep);
  --color-secondary-7:  var(--red-light);
  --color-secondary-8:  var(--red);
  --color-secondary-9:  var(--red-deep);
  --color-secondary-10: var(--green-light);
  --color-secondary-11: var(--green);
  --color-secondary-12: var(--green-deep);
  --color-secondary-13: var(--pink-light);
  --color-secondary-14: var(--pink);
  --color-secondary-15: var(--pink-deep);
  --color-neutral-1:    var(--black);
  --color-neutral-2:    var(--white);
  --color-neutral-3:    var(--linen);
  --color-neutral-4:    var(--fog);
  --color-neutral-5:    var(--stone);
  --color-neutral-6:    var(--smoke);
  --color-neutral-7:    var(--sas-gray);
  --color-neutral-8:    var(--charcoal);

  /* fluid type scale (min → max) */
  --text-2xs: clamp(0.625rem, 0.595rem + 0.10vw, 0.75rem); /* 10 → 12 */
  --text-xs: clamp(0.75rem, 0.72rem + 0.10vw, 0.875rem);   /* 12 → 14 */
  --text-s: clamp(0.875rem, 0.85rem + 0.10vw, 1rem);       /* 14 → 16 */
  --text-m: clamp(1rem, 0.97rem + 0.15vw, 1.125rem);       /* 16 → 18 */
  --text-l: clamp(1.25rem, 1.15rem + 0.30vw, 1.5rem);      /* 20 → 24 */
  --text-xl: clamp(1.5rem, 1.35rem + 0.50vw, 2rem);        /* 24 → 32 */
  --text-2xl: clamp(2rem, 1.8rem + 0.80vw, 2.5rem);        /* 32 → 40 */
  --text-3xl: clamp(2.5rem, 2.2rem + 1.00vw, 3rem);        /* 40 → 48 */
  --text-4xl: clamp(3rem, 2.7rem + 1.30vw, 4rem);          /* 48 → 64 */
  --text-5xl: clamp(3.5rem, 3.1rem + 1.60vw, 4.5rem);      /* 56 → 72 */
  --text-6xl: clamp(4rem, 3.5rem + 2.00vw, 5rem);          /* 64 → 80 */
  --text-7xl: clamp(4.5rem, 4rem + 2.50vw, 6rem);          /* 72 → 96 */

  /* line height — the four steps the blocks had converged on by hand
     (1.15 was --heading-line-height, 1.6 was --body-line-height) */
  --leading-tight: 1.15;    /* headings; absorbs the hand-written 1.1/1.2 */
  --leading-snug: 1.3;      /* card titles, tight multi-line labels */
  --leading-normal: 1.4;    /* card copy, UI text; absorbs 1.35/1.45/1.5 */
  --leading-relaxed: 1.6;   /* body copy — the site default */

  /* larger fluid spacing steps (small --spacing-0…xl defined above) */
  --spacing-0: 0;
  --spacing-xs: 0.25rem;                                    /* 4px */
  --spacing-s: 0.5rem;                                      /* 8px */
  --spacing-m: 0.75rem;                                     /* 12px */
  --spacing-l: 1rem;                                        /* 16px */
  --spacing-xl: clamp(1.25rem, 1.1rem + 0.5vw, 1.5rem);     /* 20 → 24px */
  --spacing-2xl: clamp(1.75rem, 1.5rem + 0.8vw, 2rem);      /* 28 → 32 */
  --spacing-3xl: clamp(2.5rem, 2.1rem + 1.2vw, 3rem);       /* 40 → 48 */
  --spacing-4xl: clamp(3rem, 2.5rem + 1.8vw, 4rem);         /* 48 → 64 */
  --spacing-5xl: clamp(4rem, 3.4rem + 2vw, 5rem);           /* 64 → 80 */
  --spacing-6xl: clamp(5rem, 4.3rem + 2.5vw, 6rem);         /* 80 → 96 */

  /* border radius — s/m/l absorb the hand-written 2/4/5px, 8/10px and 12/20px */
  --border-radius-s: 4px;
  --border-radius-m: 8px;
  --border-radius-l: 12px;
  --border-radius-pill: 999px;
  --border-radius-circle: 50%;

  /* elevation — one scale for every card / popover / floating control */
  --shadow-s: 0 1px 6px color-mix(in srgb, var(--color-neutral-1) 12%, transparent);
  --shadow-m: 0 2px 8px color-mix(in srgb, var(--color-neutral-1) 16%, transparent);
  --shadow-l: 0 8px 24px color-mix(in srgb, var(--color-neutral-1) 20%, transparent);

  /* motion — one curve and three durations for every state change on the site.
     Absorbs the hand-written 0.15/0.2/0.25/0.3/0.4/0.5s and the ease /
     ease-in-out / ease-out mixture the blocks had drifted into — 63 declarations
     across 19 sheets, in 27 distinct duration+easing combinations, no two blocks
     agreeing on a hover speed.

     The CURVE is one value on purpose: ease-in-out for everything, so a hover,
     a fade and a slide all decelerate the same way and the site reads as one
     surface. The DURATION is three, because they are not the same kind of
     motion — see each step. Reach for `base` unless you can say why not.

     `--card-transition` in styles/card-system.css maps `base`; a component alias like
     that is fine, a fourth duration is not. */
  --transition-fast: 0.15s ease-in-out;  /* micro-interactions: a small control's
                                            own hover / focus, an icon recolour */
  --transition-base: 0.25s ease-in-out;  /* the default: card hovers, shadows,
                                            links, backgrounds */
  --transition-slow: 0.5s ease-in-out;   /* structural motion the eye follows:
                                            a carousel track, an image filter */

  /* border width */
  --border-width-s: 1px;
  --border-width-m: 2px;
  --border-width-l: 3px;

  /* composed borders — the three that actually recur across the blocks */
  --border-s: var(--border-width-s) solid var(--color-neutral-5);  /* hairline / divider */
  --border-m: var(--border-width-m) solid var(--color-primary-1);  /* brand emphasis */
  --border-l: var(--border-width-l) solid var(--color-primary-1);  /* heavy brand */

  /* ── Form design tokens ─────────────────────────────────────────────────────
     The single source of truth for how every SAS.com form presents and behaves,
     per the forms requirements of 2026-09-08 (§9 Design System Tokens, §10
     Scalability): a form's look is changed HERE, once, and no form
     implementation is edited.

     These are a COMPONENT layer, not an addition to the design-owned palette or
     spacing scales — every value below either resolves to an existing
     --color-… / --spacing-… / --text-… / --border-… / --shadow-… /
     --transition-… token, or is geometry belonging to the control itself (a
     control's pixel height, a shake's travel) that no scale on this site names.
     Nothing here mints a new colour or a new spacing step.

     ⚠ THE NAMES ARE CHOSEN NOT TO COLLIDE WITH THE VENDORED FORMS ENGINE, and
     that constraint is load-bearing rather than stylistic. blocks/aem-form/form.css
     is upstream's stylesheet and declares its OWN `--form-…` block at `:root` —
     --form-label-font-size, --form-input-font-size, --form-error-color,
     --form-input-padding, --form-field-gap, --form-button-…, and ~50 more. It is
     imported by aem-form.css and therefore loads AFTER this file, so any name
     shared with it would be silently overwritten by upstream's value the moment
     a form appeared on the page. Every token below deliberately differs from the
     engine's spelling (ours `--form-label-size`, theirs `--form-label-font-size`),
     and `main .form` in blocks/aem-form/aem-form.css is where the engine's
     variables are mapped onto these.

     So: before adding a token here, check it is not in that `:root` block — and
     after a re-sync from ../sas-forms, check upstream has not claimed one of
     these names. A collision does not error; it just stops the token working. */

  /* sizing — the control geometry. Literals because no scale on this site names
     a form control's height; 42px and 100px are the engine's own values, stated
     here so they can be changed from one place rather than overridden. */
  --form-max-width: 760px;
  --form-input-height: 42px;
  --form-textarea-min-height: 100px;
  --form-control-size: 16px;         /* the checkbox / radio box */

  /* field spacing. --form-label-gap is 8px PER THE BRIEF (§2 "8px spacing
     between label and associated input"); it supersedes the 5px the Figma
     global-form frame measured — see the field-pitch note in aem-form.css. */
  --form-label-gap: var(--spacing-s);
  --form-field-row-gap: var(--spacing-xl);
  --form-field-column-gap: var(--spacing-3xl);
  --form-input-padding-block: var(--spacing-s);
  --form-input-padding-inline: var(--spacing-l);
  --form-action-gap: var(--spacing-l);   /* extra air above the submit row */

  /* typography. The form runs a step below body copy on purpose: a 10-field
     panel beside an article should not out-weigh it. */
  --form-font-family: var(--font-primary-1);
  --form-label-size: var(--text-xs);
  --form-label-weight: 400;
  --form-input-size: var(--text-s);
  --form-help-size: var(--text-xs);

  /* There is deliberately no form-button-size token — the button's type is the DESIGN
     SYSTEM's, not the form's.
     `.btn` in styles.css declares it, and blocks/aem-form/aem-form.css imports the
     vendored engine into a cascade layer so that declaration wins. A token here would
     advertise control this layer does not have. */

  /* border + radius */
  --form-border-width: var(--border-width-s);
  --form-border-color: var(--color-neutral-7);
  --form-border-radius: var(--border-radius-s);
  --form-control-border-color: var(--color-neutral-8);  /* checkbox / radio: a
     16px box needs the darker edge to register at all, where a 42px field would
     look boxed-in with it. Hence its own token rather than the field colour. */

  /* colors */
  --form-ink: var(--color-neutral-1);
  --form-label-ink: var(--color-neutral-1);
  --form-help-ink: var(--color-neutral-8);
  --form-placeholder-ink: var(--color-neutral-7);
  --form-surface: var(--color-neutral-2);
  --form-disabled-surface: var(--color-neutral-4);
  --form-accent: var(--color-primary-1);

  /* focus state (§5). The scale is the brief's 108%; the elevation and the
     brand border are the other two halves of the same state. */
  --form-focus-scale: 1.08;
  --form-focus-border-color: var(--color-primary-1);
  --form-focus-border-width: var(--border-width-m);
  --form-elevation-rest: none;
  --form-elevation-focus: var(--shadow-m);
  --form-focus-ring: var(--border-width-m) solid var(--color-primary-1);
  --form-focus-ring-offset: var(--border-width-m);

  /* validation state (§6). red-deep rather than red so the asterisk, the error
     note and the recoloured label all clear AA against white. */
  --form-error-ink: var(--color-secondary-9);
  --form-error-border-color: var(--color-secondary-9);
  --form-error-surface: var(--color-neutral-3);

  /* animation values (§9). --form-transition is the site's `fast` step: a
     field's own focus is a micro-interaction by that token block's own
     description. The shake is an ANIMATION, not a transition, so it names its
     own duration rather than reaching for a fourth --transition-… step; 0.4s is
     four direction changes at --form-shake-distance, which reads as a nudge
     rather than a wobble. */
  --form-transition: var(--transition-fast);
  --form-shake-distance: 3px;
  --form-shake-duration: 0.4s;

  /* dropdown (§4). The chevron is a data URI rather than /icons/…: an
     externally-referenced SVG in `background-image` cannot take a colour from
     this document, and a <select> is a replaced element with no pseudo-element
     to hang a mask on — the same two reasons the form's checkbox tick is inline.

     ⚠ The stroke is the ONE place in this block that restates a palette hex rather
     than reading a token: #454e5d is --charcoal, and a data URI cannot substitute a
     custom property (the URL is parsed as an opaque string). Recolour by editing the
     stroke, and keep it in step with --charcoal if design moves that value. */
  --form-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%23454e5d' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --form-select-chevron-size: 12px;
  --form-select-chevron-inset: var(--spacing-l);

  /* misc */
  --header-height: 64px;
  --breadcrumb-height: 24px;

}

/* 980, the header's desktop breakpoint (header.css) — the bar is 70px tall in
   its desktop layout and 64 in its mobile one, so the height steps where the
   layout does. It was 900 back when the header switched there. */
@media (width >= 980px) {
  :root {
    --header-height: 70px;
  }
}

/* Keeps a fragment target clear of whatever is fixed or stuck above it: the
   header always, and the sticky title-shelf on the pages that carry one, which
   declares --title-shelf-height at page scope for this calc to read (see
   blocks/title-shelf/title-shelf.css). Unset elsewhere, hence the 0px fallback.
   Applies to every scroll-into-view, so an authored `#guide` — see the
   section-metadata `anchor-id` key — and a heading link land the same way. */
html {
  scroll-padding-top: calc(var(--header-height) + var(--title-shelf-height, 0px) + 1rem);

  /* Fragment navigation eases to the target instead of jumping to it, so a
     `#guide` button and a heading link both read as movement down the page
     rather than as a new page.

     `scroll-behavior` is NOT inherited, so this reaches only the ROOT scroller:
     an inner scroll container — a modal body, a tab strip — keeps its own instant
     behaviour without having to opt out. The JS that already asks for smooth
     explicitly (tabs.js, aem-form) is unaffected either way, since the
     scrollIntoView option beats the property. */
  scroll-behavior: smooth;
}

/* Motion is the thing being turned off here, not an animation's duration: a
   reader who asks for less of it still has to ARRIVE at the anchor, so the jump
   comes back rather than the scroll being dropped. Written as an override of the
   rule above, the way accordion.css and tabs.css do it, rather than gating that
   rule behind `no-preference` — a browser that reports no preference at all then
   still gets the smooth scroll. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  background-color: var(--color-neutral-2);
  color: var(--color-neutral-1);
  font-family: var(--font-primary-1);
  font-size: var(--text-m);
  line-height: var(--leading-relaxed);
  padding: var(--header-height) 0 0;

  &.session {
    /* only allow font loading if a session */
    font-family: var(--font-primary-1);
  }

  &.has-breadcrumb {
    margin: calc(var(--header-height) + var(--breadcrumb-height)) 0 0;
  }

  &.no-header {
    /* `0px`, NOT a bare `0`. A unitless 0 is a <number>, and every consumer is a
       calc() that adds it to a length — `calc(0 + 0px + 1rem)` is invalid at
       computed-value time, so the declaration falls back to its initial value.
       The authoring report's `top` fell back to `auto` and pinned the box to the
       bottom of the page, out of sight; a `hero (full)`'s `100vh - 0` would have
       lost its height the same way. Same trap as `--spacing-0` in columns.css.
       One live page sets `header | off` (/en/company/contact/form/register, no
       hero), so the fix moves nothing visible but the report. */
    --header-height: 0px;

    margin: 0;
  }
}

header {
  display: none;
  height: var(--header-height);
  position: fixed;
}

a {
  color: var(--color-primary-1);

  /* The underline is always PRESENT and merely transparent until hover, because
     `text-decoration-line` is NOT an animatable property — `none` -> `underline`
     is a discrete jump, so a `transition: text-decoration` has nothing to
     interpolate and the hover lands instantly. The COLOUR is animatable, so the
     line fades in instead.

     Longhands rather than the shorthand: `text-decoration: underline` would reset
     the colour back to currentcolor and show the line at rest.

     A real text-decoration rather than a pseudo-element underline, so it follows
     every line of a WRAPPED link — the same reason filter-list.css gives for
     using one.

     `fast`, not the `base` the token block nominates for links: base is the right
     speed for a link that MOVES or changes ground (a card lifting, a pill filling),
     where the eye follows the change. This is a hairline recolouring under a cursor
     already in motion — the token block's own description of `fast`. At base it
     reads as lag on a pointer sweeping a paragraph of links.

     `:focus-visible` alongside `:hover` so the affordance is not pointer-only.

     The offset is left `auto` deliberately. A raised `<sup>` — the registered mark
     in `SAS<sup>®</sup> Viya<sup>®</sup>`, which is how most product links in this
     content are written — makes the link's line box taller, but measured against a
     fixed reference edge that does NOT move the underline: Chromium derives an
     `auto` underline from the DECORATING BOX's font metrics, so a link with a ® in
     it and one without underline at exactly the same height. Pinning an offset here
     buys nothing and only moves every underline on the site. */
  text-decoration-line: underline;
  text-decoration-color: transparent;
  transition: text-decoration-color var(--transition-fast);

  &:hover,
  &:focus-visible {
    text-decoration-color: currentcolor;
  }

  /* A link whose ENTIRE label is a <sup> or <sub> — `<a><sub>privacy@sas.com</sub></a>`,
     which is how the trust-center page authors its small print. A decoration is drawn
     by its DECORATING BOX, so an underline declared on the `a` lands on the A's
     baseline while the label's glyphs sit lowered (sub) or raised (sup): the line
     runs THROUGH the text instead of under it, surviving only as fragments between
     the letters. Hand the decoration to the sup/sub, which sits on the baseline the
     glyphs actually use.

     The `link-script-label` marker is set in scripts.js, not matched here, because
     no selector can express it — see the note there. */
  &.link-script-label {
    text-decoration-line: none;

    > :is(sup, sub) {
      text-decoration-line: underline;
      text-decoration-color: transparent;
      transition: text-decoration-color var(--transition-fast);
    }

    &:hover > :is(sup, sub),
    &:focus-visible > :is(sup, sub) {
      text-decoration-color: currentcolor;
    }
  }
}

/* ── Buttons ──────────────────────────────────────────────────────────────────
   `:is(a, button)`, not `a`. These rules used to live INSIDE the `a { }` block
   above, which meant the whole button vocabulary styled anchors only and a
   `<button class="btn btn-accent">` rendered as an unstyled UA button — no
   colour, no arrow, no hover.

   That mattered the moment a button had to BE a button. The leadership toolbox's
   "Abstract bios" control opens a dialog, and a control that opens a dialog is a
   `<button>`, not a link: a link navigates, a button acts. `modal.js` builds it
   as one, correctly. So the choice was to widen these selectors or to break the
   semantics to get the look — emit an `<a href="#…">` that does not navigate and
   fake the rest with `role="button"` and key handlers. Widening is the cheaper
   and the more accessible answer.

   Only the BUTTON subtree moved. The `a { }` block keeps its link rules — the
   brand colour and the fade-in underline — because those are wrong on a button:
   widening the outer selector instead would paint every unstyled `<button>` on
   the site brand blue.

   Blast radius today is nil: nothing in this repo put `.btn` on a non-anchor
   before this (checked across ak.js, hero, webinar and card-cta, which all
   decorate links), so no existing element starts matching. It is only new
   capability.
   ------------------------------------------------------------------------- */
:is(a, button) {
  &.btn {
    /* A button carries the SITE's body font, not whatever font the copy around
       it happens to be set in — the same values `body` declares, so an ordinary
       button is pixel-identical to what it rendered before this was written.

       It has to be declared, because a pill in ordinary default content is
       `<p><a class="btn">`, and Section Metadata `body-font-style = l` sizes
       that `<p>`; with nothing of its own the button INHERITED it and rendered
       22.72px against every other button on the site at 17.68px. One block
       declares a font on a `.btn` — blocks/side-nav sizes its rail CTA one step
       down at (0,3,0) — and nothing else does (checked across every block), so
       this overrides nothing and only stops the inheritance.

       section-metadata.css's own comment on those rows already asserts this is
       true — "headings, `strong` and `.btn` all declare their own family and
       size, so only the copy that had none follows". It was true of the other
       two and not of this one; the comment is now correct as written.

       Here rather than as an exemption in section-metadata.css because the leak
       is not that key's: any context that sizes the copy a button sits in would
       have moved it. One declaration covers all of them.

       DECLARED BELOW `font: inherit`, NOT ABOVE IT — see the note there. */
    display: inline-flex;
    text-decoration: none;
    border: var(--border-width-s) solid var(--color-primary-1);

    /* What an `<a>` gave for free and a `<button>` does not: a button carries the
       UA's own small system font and, in some browsers, an arrow cursor. Both are
       no-ops on an anchor, so they cost the existing consumers nothing.

       ORDER IS LOAD-BEARING, and the two lines under this one must stay under it.
       `font` is a SHORTHAND: it resets family, size, weight, style, line-height
       and variant, so with the family/size declared above it they were silently
       thrown away and every `.btn` went back to inheriting its size from the copy
       around it — the exact leak they exist to stop, and now on `<button>` too.
       Both intents survive in this order: the shorthand clears the UA font and
       takes weight/line-height from the context (which is what makes a `<button>`
       match an `<a>`), then the two longhands pin the family and size to the
       site's own rather than the surrounding copy's. */
    font: inherit;
    font-family: var(--font-primary-1);
    font-size: var(--text-m);
    cursor: pointer;
    padding: var(--spacing-l) var(--spacing-xl);
    box-sizing: border-box;
    border-radius: var(--border-radius-pill);
    align-items: center;
    gap: var(--spacing-s);
    transition: transform var(--transition-base);

    &:hover, &:focus-visible {
      transform: scale(1.1);
    }



    &.btn-outline {
      background: transparent;
    }
  }

  /* Text button — a plain brand-blue link with the site's arrow after it.
     Replaces the old accent pill (dark navy fill, 2px navy border).

     Authored as a bold+italic link (`***Get pricing by credential***`), which
     ak.js maps to `btn-accent`. ak.js replaces the em/strong wrapper with the
     link itself, so the label renders at regular weight; the emphasis is
     authoring syntax, not a visual instruction.

     THE CLASS NAME IS THE AUTHOR KIT'S, THE LOOK IS OURS. This used to be a
     one-line patch in scripts/ak.js making it emit `btn-text` instead — which
     worked, and silently un-worked on the next AK sync, needing a comment there
     begging the next person to re-apply it. ak.js is a synced upstream file:
     the repo already keeps the aem-form helpers out of it in scripts/sas.js for
     exactly this reason. A restyle belongs in our stylesheet, where a sync
     cannot touch it.

     `btn-text` is kept as an alias — it is the name this repo, its docs and its
     blocks say, and it costs one selector. Nothing emits it today, so if you are
     reading the DOM you will see `btn-accent`. */
  &.btn-accent,
  &.btn-text {
    /* Undo the .btn pill — this is text, not a discrete control. The
       inline-flex / align-items / gap from .btn are deliberately kept: they are
       exactly what sits the arrow beside the label. */
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;

    /* GROUND-AWARE through `--btn-accent-color`, which the GROUND sets and this
       reads. With no fill of its own, this button is a brand-blue label on
       whatever it lands on, so it disappeared on a dark section and on a
       `Columns (bg-sas-blue)` (EDGE-472). A dark section sets the hook
       (section-metadata.css), a columns ground sets it again for its own colour
       (columns.css), and a card panel puts brand blue back (card-system.css). It
       is a custom property rather than a colour applied from each ground because
       it INHERITS: the nearest ground decides, so a light columns block in a dark
       section gets blue back with no specificity contest between the two files.
       A ground that declares nothing leaves brand blue, as before. A component
       colour, not a palette token: every value it takes is an existing one. */
    color: var(--btn-accent-color, var(--color-primary-1));

    /* The site's single arrow icon, painted as a MASK so it takes the label's
       colour through currentcolor rather than the file's own #0766D1 fill — a
       background-image would look identical today but would freeze the colour
       and stop tracking the token. Same icon and same 17x18 drawn size as the
       card grids' arrow (styles/card-system.css), so the site draws one arrow.
       `align-items: center` on .btn centres it against the label's glyphs. */
    &::after {
      content: '';
      flex: 0 0 auto;
      width: 17px;
      height: 18px;
      background-color: currentcolor;
      mask: url("/icons/arrow-right.svg") no-repeat center / contain;
    }

    /* 5%, against the pill's 10%: this sits in the reading flow as text, so it
       wants a lighter touch than a button does. */
    &:hover, &:focus-visible {
      transform: scale(1.05);
    }

    /* `btn-outline` (authored as an underline) has no fill or border to outline
       here, so it collapses to the same treatment rather than doing nothing
       visible at a different colour. */
    &.btn-outline {
      color: var(--btn-accent-color, var(--color-primary-1));
      background: transparent;
    }
  }

  &.btn-primary {
    color: var(--color-neutral-2);
    background-color: var(--color-primary-1);

    &.btn-outline {
      color: var(--color-primary-1);
      background: transparent;
    }
  }

  &.btn-secondary {
    color: var(--color-primary-1);
    background-color: var(--color-neutral-2);
    border: var(--border-width-s) solid var(--color-primary-1);

    &.btn-outline {
      color: var(--color-neutral-7);
    }
  }

  &.btn-negative {
    color: var(--color-neutral-2);
    background-color: var(--color-secondary-9);
    border: var(--border-width-m) solid var(--color-secondary-9);

    &.btn-outline {
      color: var(--color-secondary-9);
    }
  }

  /* `[[Try SAS Viya]]` — Viya pink (scripts/utils/button-pink.js adds the class
     and strips the brackets). The title-shelf's pink CTA, made global.

     IT RECOLOURS, IT DOES NOT RESHAPE: brand blue becomes pink in whichever
     variant the emphasis picked, and the variant keeps its shape. So a pill
     (bare, `**…**`) is a pink fill with a white label; the secondary and an
     outline keep their ground and take a pink rule and label; the text button
     takes a pink label, and its arrow follows through `currentcolor`.

     --color-secondary-14 is the palette's `--pink`, the same token the
     title-shelf's CTA reads. White on it and it on white both measure 4.78:1,
     so every combination below passes AA for body-size text.

     (0,2,1) for the fill, which beats every single variant at (0,1,1); the
     three re-grounding cases are (0,3,1), which beats the variant-plus-outline
     pairs at (0,2,1). Specificity, not source order, so moving this block
     cannot flip it. */
  &.btn.btn-pink {
    color: var(--color-neutral-2);
    background-color: var(--color-secondary-14);
    border-color: var(--color-secondary-14);

    &:is(.btn-secondary, .btn-outline) {
      color: var(--color-secondary-14);
      background: transparent;
    }

    &.btn-secondary:not(.btn-outline) {
      background-color: var(--color-neutral-2);
    }

    &:is(.btn-accent, .btn-text) {
      color: var(--color-secondary-14);
      background: transparent;
    }
  }
}

/* ── A line of buttons stands off the copy around it ──────────────────────────
   `--spacing-xl` (20 → 24px) above a paragraph holding nothing but buttons — one
   (`**[See featured training](…)**`) or a `btn-group` — per Gray Heffner's design
   comment on EDGE-571 (2026-10-15): the buttons sat "too close to the P text… The
   spacing should be XL". The line was an ordinary `<p>`: its 1em top margin
   COLLAPSED with the 1em under the paragraph above, so a pill sat 18px under the
   copy, exactly as far as the next line of text would (measured on
   /en/services/services, the same on main).

   THE XL IS PADDING, ADDED TO THE COPY'S OWN MARGIN, never a margin collapsing
   into it. As a margin it shipped collapsing with the paragraph's 1em in an
   ordinary cell (24px) and ADDING to it in a `Columns (flex)` cell, which is a
   flex column where margins never collapse (24 + 17.68). So the same button sat
   18px further off its copy in one section than the next (Kyle Wilson, on
   /en/services/consulting: "Fast-track your AI transformation" against "Move to
   the Cloud"). With the line's own margin zeroed and the step moved to padding,
   the gap is the copy's margin plus xl in every layout: the copy's margin
   collapses with nothing, and padding never collapses.

   Scoped to copy: the section's own default content and a `columns` cell. Card
   CTAs, the hero, the title-shelf, banners and modals lay their buttons out
   themselves. Modals are excluded by name (`.modal-dialog *`), because a
   section shown as a modal (`modal = <id>`) is still a `.section` with default
   content, and the `modal` block's dialog holds columns cells.
   `:not(:first-child)` keeps a button that OPENS its cell or group flush, as
   the first-child resets there intend. `:only-child` is the button alone in its
   paragraph: decorateButton only makes a `.btn` of a link that is the
   paragraph's whole content, and a run of them is the `btn-group`. The one
   other way a `.btn` lands in a sentence is a `[[…]]` pink button, which
   button-pink.js makes whatever surrounds it; `:only-child` cannot see text, so
   that pass marks one with copy beside it `btn-inline`, and it is left out. */
:is(main .section > .default-content, main .columns .col) > p:not(:first-child, .modal-dialog *):is(.btn-group, :has(> a.btn:only-child:not(.btn-inline))) {
  margin-block-start: 0;
  padding-block-start: var(--spacing-xl);
}

/* …and BELOW it, for the same reason: the line's 1em bottom margin collapsed into
   the top margin of whatever came next, so a heading under the CTA sat ~29px off
   the pill — the heading's own margin, with nothing for the button. The same
   construction as above, so it reads the same in a flex cell as in a plain
   one: xl of padding, plus the next element's own top margin. `:not(:last-child)`
   keeps a button that ENDS its cell or group flush with the block's own padding
   below. */
:is(main .section > .default-content, main .columns .col) > p:not(:last-child, .modal-dialog *):is(.btn-group, :has(> a.btn:only-child:not(.btn-inline))) {
  margin-block-end: 0;
  padding-block-end: var(--spacing-xl);
}

/* ── Button groups ────────────────────────────────────────────────────────────
   `**[Get pricing](…)** **[Compare plans](…)**` — a paragraph whose every child is
   a link run. `decorateButton` in scripts/ak.js marks it `btn-group`; until now
   NOTHING styled that class, so the only thing between two buttons was the
   collapsed whitespace text node from the source: about 4px, with a filled pill's
   edge landing that far from its neighbour's label.

   Per meeting with Eric, 2026-09-02.

   MARGIN, not a flex `gap`, and the choice is load-bearing. Making the paragraph a
   flex container would give exact gaps but take the buttons out of inline flow —
   and `text-align: center`, which is how every centring context on this site
   centres a button row (a section `style = center`, `Columns (center)`, a centred
   hero), stops reaching them. Restoring that would mean `justify-content` rules
   enumerating every one of those contexts: a list that goes stale silently. Inline
   flow keeps centring, wrapping and alignment working with no list to maintain.

   `margin-inline-END` on all but the last, rather than `-start` on all but the
   first, so a group that WRAPS starts its second line flush instead of indented by
   the gap. The trailing margin at the end of a wrapped line is invisible.

   Two known imprecisions, both accepted: the collapsed word space still sits in
   every gap, so the rendered figure is the token plus ~4px — an 8px base renders
   nearer 12px, and at these steps that is a third of the value rather than the
   quarter it was, though it is the same ~4px in EVERY pairing so it never disturbs
   the DIFFERENCE between the two gaps below; and a wrapped CENTRED group is
   off-centre by half its trailing margin.
   ------------------------------------------------------------------------- */

.btn-group > .btn:not(:last-child) {
  margin-inline-end: var(--spacing-s);
}

/* A pill next to a text button, in either order, adds `--spacing-l` — so the base
   8px becomes 8 + 16 = 24px.

   A pill carries its inline padding INSIDE its fill, so two pills sit their labels
   `20–24 + gap + 20–24` apart. `btn-accent` zeroes that padding, so a pill beside
   one sat its labels only `20–24 + gap` apart and the bare label read as crowded
   against the fill. The addition gives most of it back.

   THE TWO STEPS ARE TUNED SEPARATELY, and that is why this is stated as "the base
   plus `--spacing-l`" rather than as "one step wider". They have moved
   independently: `--spacing-l`/`--spacing-xl` at first, then both down twice to
   `--spacing-s`/`--spacing-m`, then this term alone back up. Any phrasing that
   describes the pair as N steps apart goes stale the next time one of them moves
   on its own.

   NOT an exact compensation, deliberately. Handing back the pill's whole inline
   padding (`--spacing-xl`, the token `.btn` sets it from) would equalise the
   label-to-label distance precisely; it reads too loose. As it stands a matched pair
   sits its labels 48–56px apart and a mixed pair 44–48px, i.e. 4–8px short of equal.

   So the RELATIONSHIP is the rule here and the arithmetic is not. Retune by moving
   either step on its own, never by re-deriving one from the pill's padding: that
   derivation was true only at the very first pair of values and it is exactly what
   someone checking the numbers will reach for. Note 8 + 16 lands on 24px, which IS
   the pill's inline padding at desktop — a coincidence of the current steps, not a
   derivation, and it will stop being true at the next tweak or below 1440.

   A "pill" is any `.btn` that is not one of the two text-button names, so a bare
   `.btn`, `btn-primary`, `btn-secondary`, `btn-negative` and a pill-with-`btn-outline`
   are all included without naming them — and a variant added later is a pill by
   default, which is the safe way round.

   At (0,4,0) against the base rule's (0,3,0), and `:has(+ …)` guarantees a next
   sibling, so this can never leave a trailing margin on the last button. */
.btn-group > .btn:not(.btn-accent, .btn-text):has(+ :is(.btn-accent, .btn-text)),
.btn-group > :is(.btn-accent, .btn-text):has(+ .btn:not(.btn-accent, .btn-text)) {
  margin-inline-end: calc(var(--spacing-s) + var(--spacing-l));
}

/* Both gaps above assume the group is in INLINE FLOW — that is the whole reason they
   are margins rather than a flex `gap` (see the note above). A block that takes the
   group OUT of inline flow therefore has to hand the margin back, because an inline
   margin on a flex/grid ITEM no longer sits between two buttons: under
   `align-items: stretch` it SHRINKS the item it is set on, by the width of the gap.

   The one such block today is the modal's dialog content
   (`.modal-content .btn-group` in blocks/modal/modal.css), which stacks an
   accept/decline pair into a stretch column precisely so the two buttons share an
   edge. Left alone, the non-last button rendered 8px narrower than its neighbour in a
   matched pair and 24px narrower in a mixed one — measured 568px against 592px at
   1440, i.e. the ragged edge that rule exists to prevent.

   The exception lives HERE, next to the assumption it carves out, rather than in
   modal.css: these two rules own the inline-flow premise, so a block that breaks it is
   this section's business to answer, and one place lists every context that does.

   At (0,5,0) it beats the (0,4,0) pairing rule above outright, so it does not depend on
   stylesheet load order — block CSS happens to load after this file, but a reset that
   is only correct because of that would be one refactor away from silently failing.
   `:not(:last-child)` rather than a blanket zero, so it answers exactly the two rules
   above and cannot swallow a margin something else sets on the last button. */
.modal-dialog .modal-content .btn-group > .btn:not(:last-child) {
  margin-inline-end: 0;
}

/* ── The close button (×) of every modal and lightbox ──────────────────────────
   ONE look for every dialog's close (design review, 2026-10-13: all modals and
   lightboxes "utilize the X styling we used in the hero lightbox"): a 40px disc
   with a white ×, the disc and the glyph growing 1.2x on hover. It is the
   brightcove lightbox's close (EDGE-290, measured off the legacy site's own video
   modal), made global. Three dialogs use it — the brightcove lightbox
   (brightcove.css), the `modal` block and section modals (modal.css) and the
   carousel image lightbox (carousel.css) — and each of those sets only WHERE it
   sits, never how it looks.

   Here rather than in a block's sheet because every dialog needs it and none can
   be relied on to have loaded another block's CSS. `position: absolute` and the
   z-index are part of the look: the disc is a `::before` at z-index -1, which
   needs the button to be its own stacking context.

   THE FILL DEPENDS ON WHAT IT SITS ON, through `--close-disc-fill`:
   - over the dialog's dark scrim (every placement OUTSIDE the content, i.e. the
     desktop ones), the translucent sas-gray the legacy modal draws — on that
     ground it reads as the near-black disc design approved;
   - INSIDE the content (the small-screen placements, in a white modal card or
     over a light screenshot), translucent grey would read as a pale disc with a
     white × on it, so those placements set the 70% black the `modal` block's
     close always had. Same disc, same ×, legible on either ground.

   The hover WASH lightens (0.2, the legacy hover alpha) because on the scrim a
   lighter disc is what reads as "lit"; the growth is the cue on every ground.
   Hover only where a pointer can hover — a tap would otherwise leave the disc
   stuck grown. */
.close-disc {
  --close-disc-size: 40px;

  /* The two fills, named once. A placement INSIDE the content switches to the
     `-inside` pair: `--close-disc-fill: var(--close-disc-fill-inside)` and the
     same for the hover. */
  --close-disc-fill-scrim: color-mix(in srgb, var(--color-neutral-7) 60%, transparent);
  --close-disc-hover-fill-scrim: color-mix(in srgb, var(--color-neutral-7) 20%, transparent);
  --close-disc-fill-inside: color-mix(in srgb, var(--color-neutral-1) 70%, transparent);
  --close-disc-hover-fill-inside: color-mix(in srgb, var(--color-neutral-1) 50%, transparent);
  --close-disc-fill: var(--close-disc-fill-scrim);
  --close-disc-hover-fill: var(--close-disc-hover-fill-scrim);

  position: absolute;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--close-disc-size);
  height: var(--close-disc-size);
  padding: 0;
  border: none;

  /* Round as well as its disc, so the focus ring follows the circle rather than
     drawing a square around it. */
  border-radius: var(--border-radius-circle);
  background: none;
  color: var(--color-neutral-2);

  /* Icon geometry, not type — sized to the disc (`×` is a math symbol and inks
     at about half its em). */
  font-size: calc(var(--close-disc-size) * 1.2);
  font-weight: normal;
  line-height: 1;
  cursor: pointer;
  transition: font-size var(--transition-base);
}

.close-disc::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: var(--border-radius-circle);
  background: var(--close-disc-fill);
  box-shadow: 2px 2px 4px color-mix(in srgb, var(--color-neutral-1) 20%, transparent);
  transition: transform var(--transition-base), background-color var(--transition-base);
}

@media (hover: hover) {
  .close-disc:hover {
    font-size: calc(var(--close-disc-size) * 1.2 * 1.2);
  }

  .close-disc:hover::before {
    transform: scale(1.2);
    background: var(--close-disc-hover-fill);
  }
}

/* White on the scrim, where a brand-blue ring would not read. */
.close-disc:focus-visible {
  outline: var(--border-width-l) solid var(--color-neutral-2);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .close-disc,
  .close-disc::before {
    transition: none;
  }
}

/* ── Anchor rows ──────────────────────────────────────────────────────────────
   A line of nothing but in-page `#` links — the A–Z index on the support product
   list — marked `anchor-row` by scripts/utils/anchor-rows.js, which has the reason
   it is read in JS. Spaced exactly as a button group is, and for the same reasons:
   a margin keeps the links in inline flow so a centred row stays centred and a
   long one wraps, and `-end` on all but the last means a wrapped line starts flush.

   `--spacing-l` plus the word space the source already carries lands at ~20px,
   which is the gap the legacy page's run of non-breaking spaces drew. */
.anchor-row > a:not(:last-child) {
  margin-inline-end: var(--spacing-l);
}

/* sections */

main .section:has(> .block-content > .hero),
main .section:has(> .block-content > .full-width-banner),
main .section:has(> .block-content > .alert),
main .section:has(> .block-content > :is(.promo-banner-1, .promo-banner-2)) {
  padding: 0;
}

/* `columns (image-cover)` zeroes its section's padding too — but ONLY when every
   columns block in the section is flush.

   The reset exists because an `image-cover` block runs its media to all four of
   its own edges, so the section's vertical padding would reopen the gap above
   and below a Z-pattern the variant just closed. That holds for a section which
   IS that Z-pattern. It is wrong for a MIXED one: `:has()` matches on a single
   flush block, so one of them stripped the padding from everything else in the
   section — measured on /en/drafts/chase/company-information, a section of
   three `image-cover` blocks and three painted `bg-*` panels, where the last
   panel's bottom (3374) was the section's bottom (3374) and it butted straight
   into the linen band below with no gap at all.

   The exclusion names `.columns` specifically rather than any other block. The
   section's own `section-metadata` is a block div that removes itself at the end
   of its init, and `:has()` is live — a blanket "any other block" test would
   match it for that window and flicker the padding on and then off. Scoping to
   columns cannot see it.

   A mixed section now keeps its padding, which is the right answer: the flush
   treatment is about the BLOCK's edges, and a Z-pattern sitting among painted
   panels is content inside a section like any other. */
main .section:has(> .block-content > .columns.image-cover):not(
    :has(> .block-content > .columns:not(.image-cover))
  ) {
  padding: 0;
}

main.section {
  padding: 0;
}

/* The section's own top/bottom padding, and the only place it is set — so the
   two edges match. It was `var(--spacing-4xl) var(--spacing-2xl)`: `padding-block`
   is a start/end shorthand, so that was a 64px top against a 32px bottom, and a
   section read as sitting low in its own space. `--spacing-4xl` on both edges
   keeps the page's first TOP edge exactly where it was and raises the bottom to
   match it, so no section starts higher than it used to; the cost is that the gap
   BETWEEN two sections grows from 96px (32 + 64) to 128px (64 + 64).

   Expressed as a var() FALLBACK so Section Metadata `spacing = <step>` can win
   by simply setting the property. It could not override a padding declaration:
   `:has()` contributes its argument's specificity, making this selector (0,2,1)
   against `.section.spacing-2xl`'s (0,2,0), so an authored `spacing` row lost the
   block axis outright and reached only the inner `.default-content` margin in
   section-metadata.css — visibly doing something, but not the thing it names.

   The default lives HERE rather than in section-metadata.css because that
   stylesheet is loaded lazily (`loadStyle` in section-metadata.js, and via
   loadBlock otherwise), so a page authoring no Section Metadata anywhere would
   otherwise have no section padding at all.

   A `main .section { padding-block: 0 }` reset used to follow this rule. It was
   dead on arrival — (0,1,1) against (0,2,1), and later source order does not
   rescue a weaker selector — so it is gone rather than left implying sections
   start at zero.

   `> .section-header` is the third shape, and it is listed rather than left to
   the wrapper that header leaves behind. The block lifts itself out of its
   `.block-content` (see section-header.js), so a section holding a header and
   nothing else may end up with NO wrapper at all: `section-metadata` removes
   its own emptied wrapper too, and the two blocks decorate in parallel. When
   the header won that race the section rendered with zero padding, top and
   bottom, which is how the Documentation header on the support product pages
   came to sit flush against the band above it. */
main .section:has(> .block-content),
main .section:has(> .default-content),
main .section:has(> .section-header) {
  padding-block: var(--top-bottom-spacing, var(--spacing-4xl));
}

/* ── Every content block's own vertical spacing is `--spacing-2xl` ──────────
   One step for every block, so two blocks sit the same distance apart whichever
   two they are: 2xl + 2xl, 64px at desktop. The blocks that space themselves
   state it in their own sheet (columns, cards and the card grids, table,
   brightcove, accordion, quote, facts-figures, speaker-expert-bio, tabs, …).
   These had NO vertical spacing at all, so they sat against whatever came next.
   A block whose spacing used to live INSIDE it (the tabs' pill row carried a
   2xl top padding) states the block's spacing in its own sheet instead, so the
   two do not stack.

   `:where()` throughout, so this is a DEFAULT: (0,0,0), and any rule a block
   writes about its own padding — a variant that goes flush, a painted inset —
   wins over it. Scoped to a block group directly in a SECTION, so a block a
   layout or a panel has moved somewhere else keeps the spacing it had: the
   whitepaper layout puts its `download` button in a group inside its own
   default content, laid out to figures measured off the live page, and a tab
   panel insets its blocks itself.
   `.carousel` is the standalone slideshow: a section carousel moves its slides
   into its `.tc-track`, out of this rule's reach, and spaces itself through the
   wrapper's margin and the dot row.

   Left out on purpose: the bands and chrome that paint their own full-width
   ground (hero, title-shelf, sub-menu, alert, the promo banners,
   full-width-banner, pre-footer), the rails and page layouts (side-nav,
   social-sidebar, leadership, whitepaper, advanced-tabs), the dividers (a rule's
   gap is its own), and the forms (`form = column` lays them beside copy). */
:where(main .section > .block-content) > :where(.carousel, .filter-list, .jump-links, .listgroup-fixed, .modal, .download, .data-sheet, .next-steps, .index-search, .youtube, .typography-animation) {
  padding-block: var(--spacing-2xl);
}

/* ── A block on the section's edge gives that edge's spacing back ───────────
   The section's padding above is the ONLY space between a section's content and
   its edge. Every block also carries spacing of its own, the 2xl above, to space
   it from its neighbors inside the section — and at the section's edge that has
   no neighbor to space from, so it ADDS to the section's. Before the blocks were
   brought onto one step it was worse: a section opening with a `cards` grid
   started its cards 112px in where one opening with a heading started at 64, two
   cards sections met 224px apart, and a `quote` stood 160px off the band's edge.
   Padding never collapses,
   and a margin cannot collapse through the section's padding, so nothing else
   was ever going to absorb it. Default content already works this way — its
   first and last child lose their outer margins (the rule after this one).

   So the block that is first in the section loses its top spacing, and the block
   that is last loses its bottom. "First" is the first child of the section's
   first group; `:first-of-type` rather than `:first-child`, because a
   `background-image` row prepends a `picture` to the section, and the groups are
   all divs.

   Four things decide the shape:

   - THE BLOCKS ARE LISTED, not "whatever comes first". Several blocks put a
     different element in that place — the section carousels a `.tc-carousel`
     wrapper, the accordion its controls bar and an `.accordion-set` in place of
     itself — and a block whose spacing is part of its own design would lose it
     to a rule that cannot tell. Name what the block LEAVES BEHIND. The carousel
     wrapper's dot row is named too: its bottom margin separates the dots from
     what follows in the section, and when the carousel ends it nothing does.
     A carousel with no dots (`no-pagination`) carries that margin on the
     wrapper instead, which the list already trims.
   - A PAINTED `Columns (bg-*)` keeps its padding: there it is the inset between
     the color's edge and the copy, not spacing. "Painted" is the `PAINTED` test
     in columns.js, written out below: a `bg-*` on the block itself. `bg-split`
     and the `border-*` variants move the paint into the cells, which makes the
     block's padding spacing again, so those ARE trimmed.
   - A WIDTH-KEYED ROW trims its whole first or last LINE. `decorateLines` in
     columns.js marks it (`columns-line-first` / `-last`), because CSS cannot see
     which line a block landed on; below 900px the row stacks and the first and
     last block are the edge again. A line holding a painted block
     (`columns-line-painted`, every block on it) is left alone whole, or its
     unpainted neighbors' copy would sit 24px off the painted one's.
   - SOME SECTIONS ARE SKIPPED. `grid` and `form` sections, because the section
     grid decides which items share their first and last row and nothing marks
     them; they compound as before. And every section whose own padding is
     zeroed because its block paints a full-width band — hero, full-width-banner,
     alert, the promo banners, sub-menu, title-shelf, pre-footer, the same list
     the `padding: 0` rules above and in those blocks' sheets name. There the
     block's padding is the ONLY space at the edge, so taking it would leave
     content flush against the band (a `columns` authored above a sub-menu, on
     the Viya trial form page, went from 24px to 0). Keep this list in step with
     those rules. `.promo-columns` is listed ahead of its block: edge-456 adds it
     to the `padding: 0` rule above, and nothing here would tell whoever merges
     the second of the two branches that the lists had drifted.

   The section is hidden until every block in it has loaded (`div[data-status]`
   below), and `section-metadata` removes itself before that, so a section is
   never painted with the metadata table as its "last block". */
main .section:not(.grid, .form-column, :has(> .block-content > :is(.hero, .full-width-banner, .alert, .promo-banner-1, .promo-banner-2, .promo-columns, .sub-menu, .title-shelf, .pre-footer))) {
  > .block-content:first-of-type > :first-child:not(.sized):is(.columns:not([class*='bg-']:not(.bg-split, .border, .border-s, .border-m, .border-l)), .cards, .table, .brightcove, .video-gallery, .accordion-set, .accordion-controls, .facts-figures, .learn-more, .speaker-expert-bio, .listfromrss, .listgroup-custom, .rollover, .brand-carousel, .quote, .divider, .tc-carousel, .carousel, .tabs, .filter-list, .jump-links, .listgroup-fixed, .modal, .download, .data-sheet, .next-steps, .index-search, .youtube, .typography-animation, .aem-form:not(.form-background)) {
    padding-block-start: 0;
    margin-block-start: 0;
  }

  > .block-content:last-of-type > :last-child:not(.sized):is(.columns:not([class*='bg-']:not(.bg-split, .border, .border-s, .border-m, .border-l)), .cards, .table, .brightcove, .video-gallery, .accordion-set, .accordion-controls, .facts-figures, .learn-more, .speaker-expert-bio, .listfromrss, .listgroup-custom, .rollover, .brand-carousel, .quote, .divider, .tc-carousel, .carousel, .tabs, .filter-list, .jump-links, .listgroup-fixed, .modal, .download, .data-sheet, .next-steps, .index-search, .youtube, .typography-animation, .aem-form:not(.form-background)),
  > .block-content:last-of-type > .tc-carousel:last-child > .tc-dots,
  > .section-header:last-of-type {
    padding-block-end: 0;
    margin-block-end: 0;
  }

  /* The blocks that ARE a painted panel space themselves with a MARGIN, because
     padding there is the inset inside the color: the standalone `card`, and the
     `2-col-carousel`, which paints its own group as the waves band. So at the
     edge only the margin comes off — never the padding, which would press the
     panel's content against its own edge. */
  > .block-content:first-of-type > .card:first-child,
  > .block-content.two-col-carousel-wrapper:first-of-type {
    margin-block-start: 0;
  }

  > .block-content:last-of-type > .card:last-child,
  > .block-content.two-col-carousel-wrapper:last-of-type {
    margin-block-end: 0;
  }

  /* The width-keyed row, stacked: one block per line, so the edge is the first
     and last block again. `.columns-stack` is a `fluid` run sharing one column;
     its members are the blocks with the spacing. */
  @media (width < 900px) {
    > .block-content:first-of-type > :first-child.sized:is(.columns:not([class*='bg-']:not(.bg-split, .border, .border-s, .border-m, .border-l)), .brightcove, .typography-animation),
    > .block-content:first-of-type > .columns-stack:first-child > .columns:not([class*='bg-']:not(.bg-split, .border, .border-s, .border-m, .border-l)):first-child {
      padding-block-start: 0;
      margin-block-start: 0;
    }

    > .block-content:last-of-type > :last-child.sized:is(.columns:not([class*='bg-']:not(.bg-split, .border, .border-s, .border-m, .border-l)), .brightcove, .typography-animation),
    > .block-content:last-of-type > .columns-stack:last-child > .columns:not([class*='bg-']:not(.bg-split, .border, .border-s, .border-m, .border-l)):last-child {
      padding-block-end: 0;
      margin-block-end: 0;
    }
  }

  /* The width-keyed row, side by side: the whole first and last line, unless
     the line holds a painted block. A stack on an unpainted line holds no
     painted member, so its first or last member needs no test of its own. */
  @media (width >= 900px) {
    > .block-content:first-of-type > .columns-line-first:not(.columns-line-painted):is(.columns, .brightcove, .typography-animation),
    > .block-content:first-of-type > .columns-line-first.columns-stack:not(.columns-line-painted) > .columns:first-child {
      padding-block-start: 0;
      margin-block-start: 0;
    }

    > .block-content:last-of-type > .columns-line-last:not(.columns-line-painted):is(.columns, .brightcove, .typography-animation),
    > .block-content:last-of-type > .columns-line-last.columns-stack:not(.columns-line-painted) > .columns:last-child {
      padding-block-end: 0;
      margin-block-end: 0;
    }
  }
}

/* Default content's first and last child drop their outer margins, so the
   section's padding is the only space at its edge and a block beside the copy
   stands its own 2xl off it, with no paragraph or heading margin on top.

   section-metadata.css has always carried this same rule, but that sheet only
   loads on a page that authors a Section Metadata block. On a page without one,
   the copy's margins stayed on: a heading above a `columns` block sat 65px off
   it rather than 32 (measured on /docs/library/blocks/columns), and a section's
   first paragraph kept its 1em top margin inside the section's padding. Stated here
   so it holds on every page. `:where(main)` keeps the weight at section-metadata's
   (0,3,0), so nothing that beat that rule before is beaten by this one, and
   limits it to the page body: header and footer sections are untouched. */
:where(main) .section > .default-content > :first-child {
  margin-top: 0;
}

:where(main) .section > .default-content > :last-child {
  margin-bottom: 0;
}

/* …and the bottom margin `section-metadata.css` gives the group itself to space
   it from blocks after it, which a `spacing` row scales and which, when the
   group ends the section, has nothing after it to space from. */
main .section > .default-content:last-of-type {
  margin-block-end: 0;
}

/* A section holding nothing but a divider is a rule BETWEEN two sections, not a
   section of its own, so it takes `--spacing-xl` (20 → 24px) rather than the
   `--spacing-4xl` band every section gets — per Gray Heffner's design comment on
   EDGE-571 (2026-10-15): "the space between the dividers appears too large… page
   divider block for Viya and Standard padding should be XL". At the default the
   line floated in 128px of its own white space. The divider's own margins go
   too, by the edge rule above, so the band is the rule and 24px either side.

   The sections either side KEEP their own padding, deliberately: the divider
   adds its band to the seam between them rather than cancelling their spacing,
   so content sits the neighbor's padding plus 24px from the line (88px at
   desktop). A version that took the facing padding off both neighbors was
   tried and dropped.

   Sets the PROPERTY, as `.section.grey` does, so an authored `spacing` row still
   wins. `:where()` holds the selector at (0,1,1): above the `.section
   { --top-bottom-spacing: initial }` reset in section-metadata.css at (0,1,0),
   below `.section.spacing-*` at (0,2,0). `:only-of-type` rather than
   `:only-child` because a `background-image` row prepends a `picture`, and the
   groups are all divs. */
main .section:where(:has(> .block-content:only-of-type > .divider:only-child)) {
  --top-bottom-spacing: var(--spacing-xl);
}

.section {
  display: block;

  > .default-content {
    max-width: var(--grid-container-width);
    margin: 0 auto;
  }
}

.section.grey {
  background-color: var(--color-neutral-3);

  /* Sets the PROPERTY, not `padding` — a painted section wants its inset
     whatever the global rhythm is, and the `padding: var(--spacing-3xl) 0` this
     replaces was silently overridden on the block axis by the (0,2,1) rule
     above. At (0,2,0) it ties with `.section.spacing-2xl`, and section-metadata.css
     loads later, so an authored `spacing` row still wins. */
  --top-bottom-spacing: var(--spacing-3xl);
}

/* Legal disclaimer / fine print at the foot of an article (customer stories).
   --text-2xs, the smallest step on the scale (10 -> 12px): this is the one place
   on the site that wants to read as smaller than a caption or a field label,
   both of which sit at --text-xs. */

.section.disclaimer p {
  margin: 0;
  color: var(--color-neutral-8);
  font-size: var(--text-2xs);
  line-height: var(--leading-normal);
}

/* A lone image in its own paragraph auto-sizes to its container width —
   regardless of page — so authoring one at, say, 1500x844 in a narrow column
   never overflows it. Unscoped: this is a generic, always-sane default, not
   the article-only behavior below. */
.default-content p:has(> picture) img {
  width: 100%;
  height: auto;
  display: block;
}

/* Body images in article content — alternating float, pulling body copy up
   beside them. JS in social-sidebar.js adds .img-left to every second image
   paragraph. Default (no override class) floats right.

   Scoped to the customer-story article body — the section social-sidebar.js
   marks `has-social-sidebar` and every section after it (same sibling
   combinator social-sidebar.css already uses to extend that section's grid
   down the rest of the article) — NOT every `.default-content` site-wide. A
   lone image in an ordinary content page's default-content (no social sidebar
   anywhere before it) is plain authoring, not an article body illustration,
   and should stack in flow rather than float and pull the following
   paragraph up beside it. */
:is(.section.has-social-sidebar, .section.has-social-sidebar ~ .section) .default-content p:has(> picture) {
  float: right;
  clear: right;
  display: block;
  max-width: 42%;
  margin: 0.25rem 0 1.5rem 2rem;
  line-height: 0;
}

:is(.section.has-social-sidebar, .section.has-social-sidebar ~ .section) .default-content p:has(> picture).img-left {
  float: left;
  clear: left;
  margin: 0.25rem 2rem 1.5rem 0;
}

@media (width <= 768px) {
  :is(.section.has-social-sidebar, .section.has-social-sidebar ~ .section) .default-content p:has(> picture),
  :is(.section.has-social-sidebar, .section.has-social-sidebar ~ .section) .default-content p:has(> picture).img-left {
    float: none;
    clear: both;
    max-width: 100%;
    margin: 1.5rem 0;
    line-height: normal;
  }
}

main {
  ul,
  ol {
    padding-inline-start: 24px;

    /* Reduce nested lists even further */
    ul,
    ol {
      padding-inline-start: 12px;
    }
  }

  /* SAS blue bullet points on unordered lists (text stays default color).

     On a DARK ground the bullet follows the copy instead: brand blue on
     blue-deep is ~2.6:1, so the bullets all but vanished from the annual
     report's stats band. The colour is an inherited PROPERTY, `--bullet-color`,
     set the way `--link-color` is (EDGE-472): a dark ground sets `currentcolor`
     and a light ground inside it (a painted columns block, a card panel) sets
     brand blue back, so the NEAREST ground wins. A declared colour here would
     beat the dark ground's inherited white whatever its specificity, which is
     why this rule reads the property rather than being out-weighed. */
  ul > li::marker {
    color: var(--bullet-color, var(--color-primary-1));
  }
}

button {
  cursor: pointer;
}

li {
  margin-block: 1em;
}

h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-primary-1);
  font-weight: 400;
  line-height: var(--leading-tight);
}

h1 { font-size: var(--text-3xl); font-family: var(--font-primary-2); font-weight: 400; line-height: var(--leading-tight)} 
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-l); }
h5 { font-size: var(--text-m); }
h6 { font-size: var(--text-s); }

strong, b {
  font-family: var(--font-primary-3);
}

input,
textarea,
select,
button {
  font: inherit;
}

img {
  width: 100%;
  height: auto;
}

svg {
  width: 20px;
  height: 20px;
}

/* An icon inside a line of copy is the size of that copy, not the fixed 20px
   above — scripts/utils/inline-icons.js marks which icons those are, since only
   a test of the line's text can tell one in a sentence from one standing alone.

   0.875em, not 1em: at the full em the glyph stands taller than the capitals
   around it and reads a size up (compared side by side at 1 / 0.875 / 0.8 /
   0.75em on /en/drafts/chase/support/support-main/all-products-support; 0.8em is
   exactly cap height, if it should come down further). Height only, with
   `width: auto`, so a non-square glyph keeps its shape — the shape Section
   Metadata `icon-size` and `body-font-style` use.

   The vertical-align centres the glyph on the capitals rather than standing it
   on the baseline: these faces set a cap height of ~0.7em, so the box's middle
   belongs 0.35em up, i.e. its bottom at 0.35em - 0.875em / 2 = -0.0875em. (The
   `body-font-style` rule in section-metadata.css is the same derivation at 1em,
   measured there at -0.15em.)

   `:where()` keeps this at (0,0,1), the global rule's own weight, so it wins on
   source order alone and loses to EVERYTHING that sizes an icon on purpose — a
   block's own icon rule, Section Metadata `icon-size` and `body-font-style`. It
   corrects the default and nothing else. */
svg:where(.icon-inline) {
  height: 0.875em;
  width: auto;
  vertical-align: -0.0875em;
}

/* @media (width >= 600px) {} */

/* @media (width >= 900px) {} */

/* @media (width >= 1200px) {} */

@media (width >= 1440px) {
  :root {
    --grid-container-width: 1200px;
  }
}

main > div,
.has-template,
div[data-status] {
  display: none;
}

aem-sidekick {
  opacity: 0;

  &[open="true"].is-ready {
    opacity: 1;
  }
}

/* Brightcove video styling */
.video-js .vjs-big-play-button {
    background-size: 15%;
}
