/* ============================================================
   ENSTACKED GLOBAL UTILITIES
   Reusable section rhythm, typography, and spacing classes
   shared by ALL page templates. Values come exclusively from
   design-tokens.css — never hardcode px here.

   New templates should use the `es-` classes. The `rv-`
   aliases keep the revamped home page (and any page built
   during the revamp) working with the same single system.
   ============================================================ */

/* ── Screen-reader-only text ────────────────────────────────
   Own copy rather than legacy `.screen-reader-text`, which only
   exists in style.css and disappears once a template dequeues it. */
.es-sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Container ──────────────────────────────────────────────
   Named `.es-container` rather than `.container` so it does NOT
   collide with Bootstrap on the ~200 pages still on legacy markup.

   Geometry deliberately MIRRORS Bootstrap's `.container` ladder
   (12px inline padding, max-width capped at 1320px) so a migrated
   section lines up pixel-for-pixel with the rest of the site and
   with the homepage. When Bootstrap is finally dropped, this keeps
   the identical geometry and nothing shifts. */
.es-container {
  width: 100%;
  /* --container-gutter, not a literal, so a full-bleed child can cancel this
     exactly instead of guessing at it. Resolves to the same 12px it always
     was. See the token's note in design-tokens.css. */
  padding-inline: var(--container-gutter, 12px);
  margin-inline: auto;

  /* ── Stated here rather than inherited from Bootstrap ──────
     `width: 100%` plus 12px of inline padding overflows its parent by
     exactly 24px under the CSS default of `content-box`. It does not do that
     today only because bootstrap.min.css sets `box-sizing: border-box` on
     everything and is still enqueued site-wide.

     Section 5.4 of the brief plans to dequeue Bootstrap per template. The
     day that happens for a migrated page, every `.es-container` on it grows
     24px wider than the viewport and the page gets a horizontal scrollbar,
     which is the same class of failure the --container-gutter note in
     design-tokens.css already records once.

     Declaring it here changes nothing while Bootstrap is loaded, because it
     is the value Bootstrap already gives it. It just means the token system
     no longer depends on a stylesheet it is meant to replace.

     NOTE: this closes the dependency for the CONTAINER only. Other `es-`
     components almost certainly rely on the same global reset. Auditing that
     properly is its own job and belongs with the dequeue, not here. */
  box-sizing: border-box;
}

@media (min-width: 576px)  { .es-container { max-width: 540px;  } }
@media (min-width: 768px)  { .es-container { max-width: 720px;  } }
@media (min-width: 992px)  { .es-container { max-width: 960px;  } }
@media (min-width: 1200px) { .es-container { max-width: 1140px; } }
@media (min-width: 1400px) { .es-container { max-width: 1320px; } }

/* Narrower variants for forms and long-form reading */
.es-container--narrow { max-width: var(--container-md) !important; }
.es-container--read   { max-width: var(--reading-width) !important; }

/* ── Buttons ────────────────────────────────────────────────
   Token rewrite of the site's existing `.com-new-btn` system.
   Geometry, shadow, hover lift and the staggered triple-chevron
   pulse are matched exactly, so an `es-` section sitting above a
   legacy section shows the same button. The only differences are
   deliberate: colours resolve through tokens (dark-mode safe) and
   the icon inherits `currentColor` instead of being hardcoded
   white, so one icon works on every button variant.

   14px vertical padding on a 16px/1 label gives a 48px box,
   clearing the 44px touch-target floor. */
.es-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 28px;
  border: 1px solid transparent;
  border-radius: 50px;
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: var(--font-semibold);
  line-height: 20px;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: all var(--duration-slow) var(--ease-out);
}

.es-btn .arrow-icon,
.es-btn__icon {
  display: inline-flex;
  flex-shrink: 0;
}

.es-btn .arrow-icon path { transition: opacity var(--duration-normal) ease; }

/* Primary — matches .com-new-btn */
.es-btn--primary {
  background-color: var(--brand-500);
  color: var(--text-inverse);
  box-shadow: 0 10px 25px -10px rgba(232, 93, 32, 0.5);
}

.es-btn--primary:hover,
.es-btn--primary:focus-visible {
  background-color: var(--brand-500);
  color: var(--text-inverse);
  transform: translateY(-2px);
  box-shadow: 0 15px 35px -10px rgba(232, 93, 32, 0.6);
}

/* Secondary — matches .com-new-btn-white */
.es-btn--secondary {
  background-color: var(--bg-sunken);
  color: var(--text-heading);
}

.es-btn--secondary:hover,
.es-btn--secondary:focus-visible {
  background-color: var(--border-subtle);
  color: var(--text-heading);
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
}

.es-btn--secondary .arrow-icon { color: var(--brand-500); }

/* Outline — the same pill as the rest of the family with the fill
   taken out and a hairline in its place.

   It is a new variant rather than a reuse: `--secondary` is named
   like an outline and is not one, it is a filled `--bg-sunken` pill,
   which on a light card ground reads as a grey slab and on a sunken
   band vanishes outright. This is declared here beside the others
   rather than inside one component's stylesheet, so the next page
   that needs an outlined control takes this one instead of drawing
   a second.

   `--btn-outline-edge` is what the border and the mark go to on
   hover. It falls back to the site accent, so the variant works
   anywhere with no setup; a panel with an accent of its own sets
   that property and the button follows it. */
.es-btn--outline {
  border: 1.5px solid var(--border-default);
  background-color: transparent;
  color: var(--text-heading);
}

.es-btn--outline:hover,
.es-btn--outline:focus-visible {
  border-color: var(--btn-outline-edge, var(--brand-500));
  color: var(--text-heading);
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
}

.es-btn--outline .arrow-icon { color: var(--btn-outline-edge, var(--brand-500)); }

/* Dark — matches .com-new-btn-dark */
.es-btn--dark {
  background-color: var(--gray-900);
  color: var(--text-inverse);
}

.es-btn--dark:hover,
.es-btn--dark:focus-visible {
  background-color: var(--gray-800);
  color: var(--text-inverse);
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
}

/* Staggered chevron sweep on hover — the site's signature CTA
   microinteraction. Keyframes arrowPulse1/2/3 live in style.css;
   redeclared here so the effect survives that file being dequeued. */
.es-btn:hover .arrow-icon path:nth-child(1) { animation: arrowPulse1 0.8s infinite; }
.es-btn:hover .arrow-icon path:nth-child(2) { animation: arrowPulse2 0.8s infinite; }
.es-btn:hover .arrow-icon path:nth-child(3) { animation: arrowPulse3 0.8s infinite; }

@keyframes arrowPulse1 { 0% { opacity: .6 } 33% { opacity: 1 } 66% { opacity: .8 } 100% { opacity: .6 } }
@keyframes arrowPulse2 { 0% { opacity: .8 } 33% { opacity: .6 } 66% { opacity: 1 } 100% { opacity: .8 } }
@keyframes arrowPulse3 { 0% { opacity: 1 } 33% { opacity: .8 } 66% { opacity: .6 } 100% { opacity: 1 } }

/* Size + width modifiers */
.es-btn--sm { padding: 11px 22px; font-size: var(--fs-small); }
.es-btn--lg { padding: 18px 34px; font-size: var(--fs-lede); }

@media (max-width: 480px) {
  .es-btn--block-mobile { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .es-btn { transition: none; }
  .es-btn:hover { transform: none; }
  .es-btn:hover .arrow-icon path { animation: none; }
}

/* ── Section rhythm ─────────────────────────────────────────
   Fluid vertical padding: ~40px on mobile → 100px on desktop.
   Modifiers: --sm (compact), --lg (spacious), --flush-* (none). */
.es-section,
.rv-section { padding: var(--section-pad) 0; position: relative; }

.es-section--sm { padding-top: var(--section-pad-sm); padding-bottom: var(--section-pad-sm); }
.es-section--lg { padding-top: var(--section-pad-lg); padding-bottom: var(--section-pad-lg); }
.es-section--flush-top { padding-top: 0; }
.es-section--flush-bottom { padding-bottom: 0; }

/* Section backgrounds */
.es-bg-white, .rv-bg-white { background: var(--bg-surface); }
.es-bg-gray,  .rv-bg-gray  { background: var(--gray-50); }
.es-bg-dark,  .rv-bg-dark  { background: var(--gray-900); }
.es-bg-tint  { background: linear-gradient(180deg, var(--brand-50) 0%, var(--bg-surface) 100%); }

/* ── Section head ───────────────────────────────────────────
   Eyebrow + title + optional lede. Fluid gap to the content
   below (~36px mobile → 56px desktop). */
.es-section-head,
.rv-section-head { max-width: 780px; margin-bottom: var(--section-head-gap); }

.es-section-head.text-center,
.rv-section-head.text-center { margin-left: auto; margin-right: auto; }

/* Head gap modifiers — replaces ad-hoc inline margin overrides */
.es-section-head--tight { margin-bottom: var(--gap-fluid-sm); }
.es-section-head--flush { margin-bottom: 0; }

.es-section-head h1,
.es-section-head h2,
.rv-section-head h1,
.rv-section-head h2 {
  font-size: var(--fs-section-title);
  font-weight: 800;
  line-height: var(--lh-heading);
  letter-spacing: -0.02em;
  color: var(--text-heading);
  margin: 0;
}

.es-lede,
.rv-lede {
  font-size: var(--fs-lede);
  line-height: var(--lh-body);
  color: var(--text-body);
}

.es-section-head .es-lede,
.es-section-head .rv-lede,
.rv-section-head .es-lede,
.rv-section-head .rv-lede { margin: 20px 0 0; }

/* ── Eyebrow label ──────────────────────────────────────────
   The chip, site-wide, on the owner's instruction. It was bare
   brand-600 text on the section's own ground; it is now a rounded
   pill, which is the form the hero eyebrow and the About page's
   pillars already used. Changing it here rather than per section
   means every partial that renders `.es-eyebrow` picks it up, on
   every migrated page, with no template edits.

   ── This also closes the eyebrow contrast gap ───────────────
   The old pairing was brand-600 on whatever the section painted,
   which measured 4.47:1 against white and missed AA by 0.03 at
   12px. That single pairing was enough to hold Lighthouse
   accessibility off 100 site-wide. brand-700 (#A33A0D) on the
   brand-50 chip (#FFF3EE) measures 6.09:1, so the label now clears
   AA with room to spare regardless of what the section behind it
   is painted, because the chip brings its own ground.

   `width: fit-content` rather than `display: inline-flex`: the box
   has to shrink-wrap the label either way, and a block-level box
   can then be centred with `margin-inline: auto` below. An inline
   box would need `text-align: center` on the parent, which today
   comes from Bootstrap's `.text-center` and would break the day
   Bootstrap is dequeued. */
.es-eyebrow,
.rv-eyebrow {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-5);
  padding: var(--space-2) var(--space-5);
  border: 1px solid var(--brand-100);
  border-radius: var(--radius-full);
  background: var(--accent-subtle);
  font-family: var(--font-label);
  font-size: var(--fs-eyebrow);
  font-weight: var(--font-bold);
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--brand-700);
}

/* The 40px hairline that used to trail every eyebrow is gone, on the owner's
   instruction. The `gap` above only applies between actual children, so
   removing the pseudo-element leaves no stray spacing behind. */

/* A shrink-wrapped block centres on its margins. `justify-content` no longer
   does anything here, since the box is only as wide as its content. */
.es-section-head.text-center .es-eyebrow,
.es-section-head.text-center .rv-eyebrow,
.rv-section-head.text-center .es-eyebrow,
.rv-section-head.text-center .rv-eyebrow { margin-inline: auto; }

/* The (0N) index, where a section passes one. Inside the chip it takes the
   chip's own ink rather than --text-muted: gray-500 on brand-50 measures
   3.9:1 and would fail AA at 12px, reintroducing on the index exactly the
   problem the chip just fixed on the label. It is set apart by weight
   instead of by colour, which is also the rule about not carrying meaning
   in colour alone. */
.es-index,
.rv-index {
  color: inherit;
  font-weight: var(--font-medium);

  /* ── The marker is one token and must never be broken ──────
     The chip is a flex line, so the index is a flex item sitting beside the
     label. On a narrow screen a long label pushes the line past the
     container, both items shrink, and the marker was being split across two
     lines as "(05" then ")". Measured at 375px on the Theme Development
     page: the (04) marker rendered 24px wide and 38px tall, against 31x19
     for every marker on that page whose label was short enough to fit.

     `nowrap` removes the break opportunity inside the marker and
     `flex-shrink: 0` stops the flex line taking width off it in the first
     place, so the label absorbs the wrap on its own, which is where a wrap
     belongs. Both are needed: nowrap alone leaves the item squeezed and
     overflowing its own box.

     Not scoped to a breakpoint. A marker split in half is wrong at every
     width, and the bug only shows at narrow ones because that is where the
     line runs out of room. */
  flex-shrink: 0;
  white-space: nowrap;
}

/* Dark grounds. The chip is a light surface, so on a dark section it has to
   invert or it disappears against its own border. Kept as a translucent
   brand wash rather than a solid, so the section's artwork still reads
   through it the way it does behind the hero's eyebrow. */
.es-bg-dark .es-eyebrow,
.es-bg-dark .rv-eyebrow,
.rv-bg-dark .es-eyebrow,
.rv-bg-dark .rv-eyebrow {
  border-color: rgba(255, 157, 107, 0.28);
  background: rgba(232, 93, 32, 0.14);
  color: var(--brand-300);
}

/* Accent highlight inside headings */
.es-hl, .rv-hl { color: var(--brand-500); font-style: italic; }

/* Dark-section variants (use on .es-bg-dark sections) */
.es-bg-dark .es-section-head h1, .es-bg-dark .es-section-head h2,
.es-bg-dark .rv-section-head h1, .es-bg-dark .rv-section-head h2,
.rv-bg-dark .es-section-head h1, .rv-bg-dark .es-section-head h2,
.rv-bg-dark .rv-section-head h1, .rv-bg-dark .rv-section-head h2 { color: var(--text-inverse); }

/* ── Typography utilities ──────────────────────────────────
   For headings/copy outside a section head. */
.t-hero          { font-size: var(--fs-hero); font-weight: 800; line-height: var(--lh-display); letter-spacing: -0.02em; color: var(--text-heading); }
.t-section-title { font-size: var(--fs-section-title); font-weight: 800; line-height: var(--lh-heading); letter-spacing: -0.02em; color: var(--text-heading); }
.t-title-md      { font-size: var(--fs-title-md); font-weight: 800; line-height: var(--lh-heading); letter-spacing: -0.02em; }
.t-title-sm      { font-size: var(--fs-title-sm); font-weight: 700; line-height: var(--lh-subhead); }
.t-card-title    { font-size: var(--fs-card-title); font-weight: 800; line-height: var(--lh-subhead); }
.t-lede-lg       { font-size: var(--fs-lede-lg); line-height: var(--lh-body); }
.t-lede          { font-size: var(--fs-lede); line-height: var(--lh-body); }
.t-body          { font-size: var(--fs-body); line-height: var(--lh-body); }
.t-small         { font-size: var(--fs-small); }
.t-caption       { font-size: var(--fs-caption); }

/* ── Spacing utilities ─────────────────────────────────────
   Token scale: 0=0  1=4px  2=8px  3=12px  4=16px
                5=24px  6=32px  7=48px  8=64px
   !important so they reliably win over component CSS —
   use them to replace inline style="" overrides. */
.u-mt-0 { margin-top: 0 !important; }
.u-mt-1 { margin-top: var(--space-1) !important; }
.u-mt-2 { margin-top: var(--space-2) !important; }
.u-mt-3 { margin-top: var(--space-3) !important; }
.u-mt-4 { margin-top: var(--space-4) !important; }
.u-mt-5 { margin-top: var(--space-5) !important; }
.u-mt-6 { margin-top: var(--space-6) !important; }
.u-mt-7 { margin-top: var(--space-7) !important; }
.u-mt-8 { margin-top: var(--space-8) !important; }

.u-mb-0 { margin-bottom: 0 !important; }
.u-mb-1 { margin-bottom: var(--space-1) !important; }
.u-mb-2 { margin-bottom: var(--space-2) !important; }
.u-mb-3 { margin-bottom: var(--space-3) !important; }
.u-mb-4 { margin-bottom: var(--space-4) !important; }
.u-mb-5 { margin-bottom: var(--space-5) !important; }
.u-mb-6 { margin-bottom: var(--space-6) !important; }
.u-mb-7 { margin-bottom: var(--space-7) !important; }
.u-mb-8 { margin-bottom: var(--space-8) !important; }

.u-pt-0 { padding-top: 0 !important; }
.u-pt-1 { padding-top: var(--space-1) !important; }
.u-pt-2 { padding-top: var(--space-2) !important; }
.u-pt-3 { padding-top: var(--space-3) !important; }
.u-pt-4 { padding-top: var(--space-4) !important; }
.u-pt-5 { padding-top: var(--space-5) !important; }
.u-pt-6 { padding-top: var(--space-6) !important; }
.u-pt-7 { padding-top: var(--space-7) !important; }
.u-pt-8 { padding-top: var(--space-8) !important; }

.u-pb-0 { padding-bottom: 0 !important; }
.u-pb-1 { padding-bottom: var(--space-1) !important; }
.u-pb-2 { padding-bottom: var(--space-2) !important; }
.u-pb-3 { padding-bottom: var(--space-3) !important; }
.u-pb-4 { padding-bottom: var(--space-4) !important; }
.u-pb-5 { padding-bottom: var(--space-5) !important; }
.u-pb-6 { padding-bottom: var(--space-6) !important; }
.u-pb-7 { padding-bottom: var(--space-7) !important; }
.u-pb-8 { padding-bottom: var(--space-8) !important; }

/* ── Scroll-reveal animation ────────────────────────────────
   Standard entrance used across templates. Add `.reveal-up`
   to any element; JS toggles `.is-visible` on scroll-in.
   Stagger with inline style="--rv-delay: .1s". Respects
   reduced-motion. */
.reveal-up {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .7s cubic-bezier(.22,.61,.36,1),
              transform .7s cubic-bezier(.22,.61,.36,1);
  transition-delay: var(--rv-delay, 0s);
}

.reveal-up.is-visible { opacity: 1; transform: none; }

/* Pop entrance. Same trigger and the same --rv-delay stagger as .reveal-up,
   but scaled up from small instead of lifted from below, with a short
   overshoot on the way in. For a chip or badge that belongs to the thing it
   sits on, arriving in place reads better than sliding in from underneath.
   Do not put this on anything that carries its own transform for placement:
   the entrance owns the transform property. */
.reveal-pop {
  opacity: 0;
  transform: scale(.82);
  transition: opacity .45s cubic-bezier(.22,.61,.36,1),
              transform .45s cubic-bezier(.34,1.56,.64,1);
  transition-delay: var(--rv-delay, 0s);
}

.reveal-pop.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal-up,
  .reveal-pop { opacity: 1; transform: none; transition: none; }
}

/* Fluid spacing utilities — scale with viewport */
.u-mt-fluid-xs { margin-top: var(--gap-fluid-xs) !important; }
.u-mt-fluid-sm { margin-top: var(--gap-fluid-sm) !important; }
.u-mt-fluid-md { margin-top: var(--gap-fluid-md) !important; }
.u-mt-fluid-lg { margin-top: var(--gap-fluid-lg) !important; }
.u-mt-fluid-xl { margin-top: var(--gap-fluid-xl) !important; }

.u-mb-fluid-xs { margin-bottom: var(--gap-fluid-xs) !important; }
.u-mb-fluid-sm { margin-bottom: var(--gap-fluid-sm) !important; }
.u-mb-fluid-md { margin-bottom: var(--gap-fluid-md) !important; }
.u-mb-fluid-lg { margin-bottom: var(--gap-fluid-lg) !important; }
.u-mb-fluid-xl { margin-bottom: var(--gap-fluid-xl) !important; }

/* ── Scrolling ──────────────────────────────────────────────
   style.css:3 already sets `scroll-behavior: smooth` on the
   root, site wide. Two things it is missing, added here rather
   than there because utilities.css loads last and legacy CSS is
   not edited without a reason to be in it.

   1. A landing offset. The header is `position: fixed`, so a
      jump to an in-page anchor puts the target under the bar:
      measured on the case study contents rail, the heading
      landed 96px above the top of its own text. `scroll-padding`
      on the scroll container is the property for that, and it
      fixes every anchor on the site at once rather than asking
      each target to carry its own `scroll-margin`.

   2. A reduced-motion escape. Smooth scrolling animates every
      jump, and for a visitor who has asked the system for less
      motion an animated jump is the thing they asked not to
      have. `auto` is instant, not broken. */
:root {
  scroll-padding-top: var(--sticky-offset);
}

@media (prefers-reduced-motion: reduce) {
  :root { scroll-behavior: auto; }
}
