/* =========================================================================
   ENSTACKED — SITE HEADER
   Full-bleed glass bar + one morphing mega-menu card + mobile drawer.

   Layout reproduces the enstacked2.0 navigation design: a full-width bar
   that is transparent at the top of the page and turns to glass once the
   user scrolls, a SINGLE stage card underneath that morphs its width and
   slides horizontally to sit under the active trigger, a blurred backdrop,
   and a full-card mobile drawer built on <details> accordions.

   Three deliberate differences from the reference, all for site uniformity:

     - every colour, radius, space and easing resolves through a design
       token, so the header survives the dark-mode block when it lands. The
       one exception is --esn-glass below, which has no token because the
       token set has no translucent surface yet;

     - the container caps at 1320px, not the reference's 1200px. 1320px is
       what `.es-container` and Bootstrap's `.container` cap at, so the logo
       and the CTA line up edge-for-edge with page content on wide screens.
       A 1200px bar would sit 60px inside the content column on both sides,
       which is visible on any page with a full-width hero;

     - the drawer takes over at 991.98px, not the reference's 900px, because
       that is the breakpoint every other component on this site flips at.

   Class prefix `esn-` throughout. The reference names (.brand, .eyebrow,
   .badge, .backdrop, .link-card, .acc) are generic enough to collide with
   the ~200 legacy pages still running the old stylesheet, so nothing here
   uses a bare name. The `data-*` hooks ARE the contract with nav.js.
   ========================================================================= */

/* Header-local layout constants, declared once and shared by all three
   fixed pieces. Not in :root and not in design-tokens.css: these are this
   component's geometry, not site-wide design tokens. The drawer needs them
   too (it hangs off the bottom of the bar) and it is a SIBLING of the
   header, not a descendant, so it cannot inherit them. */
.esn-header,
.esn-drawer {
  --esn-h: 64px;                 /* bar height                            */
  --esn-offset: var(--space-3);  /* 12px — inset from the viewport edge   */
  --esn-gap: var(--space-2);     /* 8px  — bar to stage card              */
  --esn-max: 1320px;             /* matches `.es-container`               */

  /* ── Frosted surfaces ──────────────────────────────────────────────
     Not tokens because design-tokens.css has no translucent surface yet.
     These four are SEMANTIC: the dark block further down only reassigns
     them, so `.esn-stage` and `.esn-drawer` never name a ground directly
     and the two themes cannot drift apart.

     ONE layer each, and the surface colour rather than white. There was a
     white gradient washed over a --gray-50 floor here; it made the pane read
     as a white card with a blur behind it rather than as glass, and the
     second layer bought nothing the alpha could not.

     0.84 on the panel is not a taste call, it is the floor. Light pages carry
     inset --gray-900 objects (the closing CTA panel, the bento tiles), and
     when one of those is behind the pane the ink lands on about #D6D5D3.
     --brand-700, the hover ink, needs 0.84 to hold 4.52:1 there; --text-muted
     needs 0.82. Below that the panel is glassier and the text fails AA. The
     bar sits at 0.80 because nothing on it is muted or brand-coloured: it
     carries --text-color at 11.66:1 and an opaque pill.

     No inset highlight on this theme. A white top edge is what makes a dark
     pane read as glass; on a near-white pane it is invisible, so --esn-edge
     resolves to nothing here and only the dark block gives it a value. */
  --esn-glass: rgba(250, 250, 248, 0.80);
  --esn-glass-solid: var(--esn-panel);
  --esn-panel: rgba(250, 250, 248, 0.84);

  --esn-line: rgba(22, 20, 16, 0.10);
  --esn-edge: transparent;

  /* The dark-ground set: frosted glass with a white tint, not a black scrim.

     Each is TWO layers. A white gradient carries the tint and the top-lit
     falloff that makes a pane read as glass; a --gray-900 wash underneath is
     the safety net, because `backdrop-filter` on a descendant of an element
     that already has one is not reliable (the ancestor establishes a backdrop
     root and the descendant can sample nothing). With the wash, a panel whose
     blur fails to resolve is merely flat instead of see-through.

     The white tint lands the panel around #32302D over a --gray-900 hero,
     which is what every ink value in the on-dark block is measured against. */
  --esn-glass-dark:
    linear-gradient(180deg, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.04)),
    rgba(22, 20, 16, 0.72);
  --esn-panel-dark:
    linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.05)),
    rgba(22, 20, 16, 0.82);

  --esn-line-dark: rgba(255, 255, 255, 0.18);
  --esn-edge-dark: rgba(255, 255, 255, 0.14);   /* the lit top edge of a pane */

  /* Objects sitting ON the glass. An interactive surface LIFTS: the white
     stacks on top of the pane's own tint, so a hovered row composites to
     about #464542 against the pane's #32302D and the change is legible
     without a border. A plate or a well SINKS instead, which is the
     separation the old near-black panel got for free and this one has to
     draw on purpose. */
  --esn-fill-dark: rgba(255, 255, 255, 0.10);
  --esn-sunk-dark: rgba(22, 20, 16, 0.55);
  --esn-sunk-dark-2: rgba(22, 20, 16, 0.35);
}

/* ── Shell ───────────────────────────────────────────────────────────── */

.esn-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background var(--duration-slow) var(--ease-out),
              border-color var(--duration-slow) var(--ease-out),
              -webkit-backdrop-filter var(--duration-slow) var(--ease-out);
  transition: background var(--duration-slow) var(--ease-out),
              border-color var(--duration-slow) var(--ease-out),
              backdrop-filter var(--duration-slow) var(--ease-out);
}

/* Glass + hairline once the page has moved, or while a panel is open. */
.esn-header.is-scrolled,
.esn-header.is-open {
  background: var(--esn-glass);
  border-bottom-color: var(--esn-line);
  /* Same blur as the panel below it. The bar and the stage are two panes of
     the same glass and are visible together whenever a menu is open, so a
     different radius on each reads as a mistake. */
  -webkit-backdrop-filter: saturate(180%) blur(32px);
          backdrop-filter: saturate(180%) blur(32px);
}

/* Opaque while a panel is open, so the bar and the stage card below it
   read as one surface rather than two translucent sheets. */
.esn-header.is-open { background: var(--esn-glass-solid); }

/* ── Bar ─────────────────────────────────────────────────────────────── */

.esn-inner {
  position: relative;
  max-width: var(--esn-max);
  margin: 0 auto;
  height: var(--esn-h);
  padding: 0 var(--space-5);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-6);
}

@media (min-width: 1280px) {
  .esn-inner { padding-left: var(--space-6); padding-right: var(--space-6); }
}

.esn-brand {
  display: inline-flex;
  align-items: center;
  height: 100%;
}

/* No hover treatment on the brand mark, on the owner's instruction,
   2 Sep 2026. The 1.02 scale-up and the transition that drove it are both
   gone; `transform-origin` went with them because it existed only to anchor
   that scale. The logo is a fixed-width wordmark, and nudging it on hover
   read as a wobble rather than as feedback. The link is already obvious. */
.esn-brand img {
  width: 140px;
  height: auto;
}

/* ── Primary nav ─────────────────────────────────────────────────────── */

.esn-nav { justify-self: center; height: 100%; }

.esn-list {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  height: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

.esn-item {
  display: inline-flex;
  align-items: center;
  height: 100%;
}

.esn-trigger,
.esn-navlink {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  padding: 0 var(--space-4);
  border: 0;
  background: none;
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  font-weight: var(--font-medium);
  line-height: 1;
  letter-spacing: var(--tracking-normal);
  color: var(--text-color);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--duration-normal) var(--ease-out);
}

/* --brand-700, not --brand-500 or --brand-600, for any brand-coloured TEXT
   in here. utilities.css already worked this out for `.es-eyebrow`: brand-600
   measures 4.47:1 on white and misses AA by 0.03, and brand-500 is 3.4:1.
   Making the panes translucent tightens it further, because a dark section
   scrolling under one lifts the ground the ink sits on. brand-700 holds
   4.95:1 even in that worst case.

   The brand-500 that is left in this file is all NON-text: the 2px rule under
   a trigger, the CTA's top hairline, the chevron glyphs. Those answer to the
   3:1 bar for graphical objects, which brand-500 clears. */
.esn-trigger:hover,
.esn-navlink:hover,
.esn-trigger[aria-expanded="true"] { color: var(--brand-700); }

.esn-caret {
  flex-shrink: 0;
  opacity: 0.6;
  transition: transform var(--duration-slow) var(--ease-out),
              opacity var(--duration-normal) var(--ease-out);
}

.esn-trigger:hover .esn-caret,
.esn-trigger[aria-expanded="true"] .esn-caret { opacity: 1; }
.esn-trigger[aria-expanded="true"] .esn-caret { transform: rotate(-180deg); }

/* Brand rule under the open trigger. The two standalone links draw the
   same rule on hover, which is the only indicator they get: they have no
   caret to rotate. */
.esn-trigger::after,
.esn-navlink::after {
  content: "";
  position: absolute;
  left: var(--space-4);
  right: var(--space-4);
  bottom: 6px;
  height: 2px;
  border-radius: 2px;
  background: var(--brand-500);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--duration-slow) var(--ease-out);
}

.esn-navlink:hover::after { transform: scaleX(0.45); }
.esn-trigger[aria-expanded="true"]::after { transform: scaleX(1); }

/* ── Bar actions ─────────────────────────────────────────────────────── */

.esn-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  justify-self: end;
}

/* The header CTA is the reference navbar's compact pill, on the owner's
   instruction: a 36px capsule with an "ink wipe" hover (a solid fill rising
   from the bottom while a brand hairline draws across the top edge) and the
   up-right glyph.

   It is deliberately NOT `.es-btn--dark`, the site section button. That one is
   16px type in 14px-28px padding, which is 50px tall and 171px wide; sized for
   a section CTA it is the heaviest object in a 64px bar. The padding here is
   12px, down from that 28px, which is where the white space went.

   Colour, motion tokens and the brand hairline all still resolve through the
   design tokens, so only the geometry and the hover belong to the reference. */
.esn-cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 36px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-full);
  background: var(--gray-900);
  color: var(--text-inverse);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: var(--font-semibold);
  line-height: 1;
  letter-spacing: var(--tracking-normal);
  white-space: nowrap;
  text-decoration: none;
  overflow: hidden;
  isolation: isolate;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--duration-slow) var(--ease-out),
              background-color var(--duration-slow) var(--ease-out),
              border-color var(--duration-slow) var(--ease-out),
              color var(--duration-normal) var(--ease-out);
}

/* Rising fill. z-index -1 inside the button's own stacking context paints it
   above the button's background and below its label. */
.esn-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--gray-800);
  transform: translateY(101%);
  transition: transform var(--duration-slow) var(--ease-out);
}

.esn-cta::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1.5px;
  background: var(--brand-500);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--duration-slow) var(--ease-out);
}

/* The up-right glyph that sat beside the label was removed on the owner's
   instruction, 2 Sep, so `.esn-cta__icon` no longer exists in the markup and
   its rules are gone with it rather than being left to rot. The label keeps
   the stacking context it needs to sit above the rising fill. */
.esn-cta > span { position: relative; z-index: 1; }

.esn-cta:hover,
.esn-cta:focus-visible { color: var(--text-inverse); }
.esn-cta:hover::before,
.esn-cta:focus-visible::before { transform: translateY(0); }
.esn-cta:hover::after,
.esn-cta:focus-visible::after { transform: scaleX(1); }
.esn-cta:active { transform: translateY(1px); }

/* The drawer's CTA is the one place it is a full-width block, where the
   compact height would read as an afterthought. */
.esn-cta--block {
  width: 100%;
  height: 52px;
  border-radius: var(--radius-lg);
  font-size: var(--fs-body-sm);
}


/* Hamburger */
.esn-toggle {
  display: none;
  position: relative;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: var(--radius-full);
  background: none;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  transition: background-color var(--duration-normal) var(--ease-out);
}

.esn-toggle:hover { background: var(--bg-sunken); }

.esn-toggle .esn-bar {
  position: absolute;
  left: 14px;
  width: 16px;
  height: 1.8px;
  border-radius: 2px;
  background: var(--text-color);
  transition: transform var(--duration-slow) var(--ease-out),
              opacity var(--duration-normal) var(--ease-out),
              top var(--duration-slow) var(--ease-out);
}

.esn-toggle .esn-bar:nth-child(1) { top: 16px; }
.esn-toggle .esn-bar:nth-child(2) { top: 21px; }
.esn-toggle .esn-bar:nth-child(3) { top: 26px; }

.esn-toggle[aria-expanded="true"] .esn-bar:nth-child(1) { top: 21px; transform: rotate(45deg); }
.esn-toggle[aria-expanded="true"] .esn-bar:nth-child(2) { opacity: 0; }
.esn-toggle[aria-expanded="true"] .esn-bar:nth-child(3) { top: 21px; transform: rotate(-45deg); }

/* ── Mega stage ──────────────────────────────────────────────────────────
   ONE card for every panel. nav.js unhides the incoming panel, narrows the
   card to that panel's `data-width`, slides it under the active trigger via
   --esn-stage-x, then writes the measured height. Everything below is the
   transition; no per-menu geometry lives in this file. */

.esn-stage {
  position: absolute;
  top: calc(var(--esn-h) + var(--esn-gap));
  left: var(--esn-offset);
  right: var(--esn-offset);
  margin: 0 auto;
  max-width: var(--esn-max);
  height: 0;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
  background: var(--esn-panel);
  border: 1px solid var(--esn-line);
  border-radius: var(--radius-xl);
  /* The inset hairline is the lit top edge of the pane. It is what separates
     frosted glass from a flat translucent rectangle, and it costs nothing. */
  box-shadow: var(--shadow-xl), inset 0 1px 0 var(--esn-edge);
  -webkit-backdrop-filter: saturate(180%) blur(32px);
          backdrop-filter: saturate(180%) blur(32px);
  transform: translateX(var(--esn-stage-x, 0px));
  will-change: height, max-width, transform, opacity;
  transition: height var(--duration-slow) var(--ease-out),
              max-width var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out),
              opacity var(--duration-normal) var(--ease-out);
}

.esn-header.is-open .esn-stage {
  opacity: 1;
  pointer-events: auto;
}

.esn-stage-inner {
  position: relative;
  padding: 0 var(--space-6);
}

/* The footer rule already closes the panel, so the 48px that used to sit
   under it read as the card failing to end. */
.esn-panel { padding: var(--space-6) 0 var(--space-5); }

/* The UA default for [hidden] is display:none, but Bootstrap's reboot and
   several legacy rules set display on generic selectors; restate it so a
   closed panel can never contribute height to the stage. */
.esn-panel[hidden] { display: none; }

/* A panel's own width is pinned in pixels by nav.js on every open and every
   resize, so the height it measures is the height it will need once the
   card's width transition has finished. Nothing declares a panel width
   here: `data-width` in dynamic-header.php is the only source of it. */

@keyframes esnPanelIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

.esn-panel { animation: esnPanelIn var(--duration-slow) var(--ease-out) both; }

/* ── Shared panel typography ─────────────────────────────────────────── */

/* ONE label for every panel, on the owner's instruction: the flat uppercase
   form the Shopify tab's group headings already used, not `.es-eyebrow`.

   This is not a second chip. The site chip stays exactly as it is and stays
   the eyebrow on every section of every page; the nav panels simply do not
   use it. Six brand pills inside one dropdown put more weight on the labels
   than on the links under them, which is what the owner was looking at when
   they called it. The typography is still the chip's (label face, 12px, bold,
   1.8px tracking, uppercase), so the two read as one family. */
.esn-label {
  display: block;
  margin: 0 0 var(--space-3);
  font-family: var(--font-label);
  font-size: var(--fs-eyebrow);
  font-weight: var(--font-bold);
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Over the service rows rather than beside them, so it takes their inner
   padding and the label sits on the same left edge as the links. */
.esn-grouplabel {
  margin-bottom: var(--space-1);
  padding-left: var(--space-4);
}

/* On the featured card's --gray-900 ground. --gray-400 is 7.80:1 there and is
   the label colour section-hero.php already measured for that surface. */
.esn-label--on-dark { color: var(--gray-400); }

.esn-heading {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-title-sm);
  font-weight: var(--font-bold);
  line-height: var(--lh-heading);
  letter-spacing: var(--tracking-tight);
  color: var(--text-heading);
}

.esn-sub {
  margin: 0;
  max-width: 38ch;
  font-size: var(--fs-body-sm);
  line-height: var(--leading-normal);
  color: var(--text-body);
}

/* ── Panel grid (White-Label, Company) ───────────────────────────────── */

.esn-grid {
  display: grid;
  gap: var(--gap-fluid-lg);
}

.esn-grid--duo { grid-template-columns: 1.05fr 1fr; }

.esn-lead {
  display: flex;
  flex-direction: column;
  padding-right: var(--space-2);
}

.esn-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin: var(--space-5) 0 0;
  padding: var(--space-4) 0;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
}

.esn-stats > div {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.esn-stats > div + div::before {
  content: "";
  position: absolute;
  left: -8px;
  top: 4px;
  bottom: 4px;
  width: 1px;
  background: var(--border-subtle);
}

.esn-stats dt {
  font-size: var(--text-xl);
  font-weight: var(--font-bold);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  color: var(--text-heading);
  font-feature-settings: "tnum", "lnum";
}

.esn-stats dd {
  margin: 0;
  font-size: var(--fs-eyebrow);
  color: var(--text-body);
}

/* Proof list. Used where the panel has claims rather than figures. */
.esn-proof {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-5) 0 0;
  padding: var(--space-4) 0;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
  list-style: none;
}

.esn-proof li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-caption);
  font-weight: var(--font-medium);
  color: var(--text-heading);
}

.esn-proof__icon { flex-shrink: 0; color: var(--brand-500); }

.esn-leadlink {
  align-self: flex-start;
  margin-top: auto;
  padding-top: var(--space-5);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-caption);
  font-weight: var(--font-semibold);
  color: var(--text-heading);
  text-decoration: none;
  transition: gap var(--duration-normal) var(--ease-out),
              color var(--duration-normal) var(--ease-out);
}

.esn-leadlink:hover { color: var(--brand-700); }

/* Every text CTA in the header carries the site's signature CTA
   microinteraction: the triple chevron's three paths sweeping their opacity
   in sequence (arrowPulse1/2/3, defined in utilities.css). It replaces the
   translate(2px,-2px) nudge the reference navbar used, which appears nowhere
   else on this site. `gap` is not animated any more either: the site's own
   buttons hold their gap and move the glyph, not the box. */
.esn-link__icon {
  flex-shrink: 0;
  color: var(--brand-500);
}

.esn-leadlink .arrow-icon path,
.esn-panefoot__cta .arrow-icon path,
.esn-ghost .arrow-icon path { transition: opacity var(--duration-normal) ease; }

.esn-leadlink:hover .arrow-icon path:nth-child(1),
.esn-panefoot__cta:hover .arrow-icon path:nth-child(1),
a.esn-feature:hover .esn-ghost .arrow-icon path:nth-child(1) { animation: arrowPulse1 0.8s infinite; }
.esn-leadlink:hover .arrow-icon path:nth-child(2),
.esn-panefoot__cta:hover .arrow-icon path:nth-child(2),
a.esn-feature:hover .esn-ghost .arrow-icon path:nth-child(2) { animation: arrowPulse2 0.8s infinite; }
.esn-leadlink:hover .arrow-icon path:nth-child(3),
.esn-panefoot__cta:hover .arrow-icon path:nth-child(3),
a.esn-feature:hover .esn-ghost .arrow-icon path:nth-child(3) { animation: arrowPulse3 0.8s infinite; }

/* ── Link cards ──────────────────────────────────────────────────────── */

.esn-cards {
  display: flex;
  flex-direction: column;
  align-content: start;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.esn-card {
  position: relative;
  display: grid;
  grid-template-columns: 44px 1fr;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  text-decoration: none;
  transition: background-color var(--duration-normal) var(--ease-out);
}

/* One hover for every link row in the panels: the brand-50 tint the site uses
   for an interactive surface, and nothing else. This is exactly what
   `a.esn-service` (the Shopify rows) does, so a White-Label or Company row now
   answers the pointer the same way a Shopify one does. The lift and the icon
   chip's brand flip that used to live here were the only things making the two
   read as different objects. The watermark glyph is NOT copied across: it
   stands in for the icon chip the Shopify rows do not have, and a row that
   already carries a chip does not need a second mark. */
/* The row goes GREY, the icon goes BRAND. Owner's call, 2 Sep, and it is the
   better division of labour: a brand-tinted row puts the accent on 300px of
   surface to say one row is hovered, which is the loudest way to say the
   quietest thing. Moving it onto the 44px chip says the same thing with a
   fortieth of the area, and leaves the tint free to mean something else.

   `--bg-sunken` rather than a new grey: it is the same ground the icon chip
   already sits on at rest, so the hover reads as the chip and the row trading
   places rather than as two unrelated colour changes. */
a.esn-card:hover { background: var(--bg-sunken); }

a.esn-card:hover .esn-card__icon {
  background: var(--accent-subtle);
  /* brand-600, not brand-500. The glyph is a graphical object and only owes
     3:1, which brand-500 clears, but this chip sits directly under a
     brand-700 title on some rows and two oranges a step apart read as a
     mistake. brand-600 on the brand-50 chip measures 4.3:1. */
  color: var(--brand-600);
}

.esn-card__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  background: var(--bg-sunken);
  color: var(--text-heading);
}

.esn-card__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.esn-card__title {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-body-sm);
  font-weight: var(--font-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--text-heading);
}

.esn-card__desc {
  font-size: var(--fs-caption);
  line-height: 1.45;
  color: var(--text-body);
}

/* Unpublished destination: a row, not a link, so a global nav can never
   ship a 404. Give the item a `url` in dynamic-header.php to switch it on. */
.esn-card.is-soon { cursor: default; }
.esn-card.is-soon .esn-card__icon,
.esn-card.is-soon .esn-card__title { color: var(--text-body); }

/* Reads as a small sibling of `.es-eyebrow`: same label face, same brand-50
   ground, same brand ink. --fs-eyebrow is documented in design-tokens.css as
   the floor ("nothing on the site is smaller"); this carried 10px. */
.esn-badge {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  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-style: normal;
  font-weight: var(--font-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--brand-700);
}

/* ── Shopify panel ───────────────────────────────────────────────────── */

.esn-mega {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--gap-fluid-lg);
}

.esn-groups {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--space-6);
  row-gap: var(--space-5);
  align-content: start;
}

/* Hairline between the two lifecycle columns. */
.esn-group:nth-child(even) {
  border-left: 1px solid var(--border-subtle);
  padding-left: var(--space-6);
}

/* Group-label spacing lives on .esn-grouplabel itself. */

.esn-services {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.esn-service {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  overflow: hidden;
  isolation: isolate;
  text-decoration: none;
  transition: background-color var(--duration-normal) var(--ease-out);
}

/* Same trade as `a.esn-card` above. These rows carry no icon chip, so the
   orange lands on the watermark glyph instead, which already fades in on
   hover in `--brand-500` and now does it against a neutral ground where it
   actually reads. */
a.esn-service:hover { background: var(--bg-sunken); }

.esn-service b {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-body-sm);
  font-weight: var(--font-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--text-heading);
}

.esn-service > span {
  position: relative;
  z-index: 1;
  font-size: var(--fs-caption);
  line-height: 1.45;
  color: var(--text-body);
}

.esn-service.is-soon b,
.esn-service.is-soon > span { color: var(--text-body); }

/* Watermark glyph. Sits behind the label at the row's top-right and fades
   up on hover; that is what lets eight rows fit where four icon cards
   would, without the panel losing its iconography. */
.esn-service__mark {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 0;
  color: var(--brand-500);
  opacity: 0;
  transform: scale(0.85);
  transform-origin: top right;
  pointer-events: none;
  transition: opacity var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out);
}

a.esn-service:hover .esn-service__mark { opacity: 0.18; transform: scale(1); }

/* ── Products panel ──────────────────────────────────────────────────────
   Varn and Caffenza are Enstacked's own app and theme, not client work, so
   they get their own panel rather than a teaser inside Shopify. Neither is
   published: the cards render as plain elements with the "Coming soon" chip
   until an item is given a `url` in dynamic-header.php. */

/* The lead column gives up width here: two cards carrying a plate, a name and
   a tag need more room than three lines of copy do, and at the old split each
   card was ~175px, which is where "too small" came from. */
.esn-panel[data-width="920"] > .esn-grid--duo { grid-template-columns: 0.8fr 1.2fr; }

.esn-products {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  align-content: start;
  margin: 0;
  padding: 0;
  list-style: none;
}

.esn-product {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  overflow: hidden;
  text-decoration: none;
  transition: transform var(--duration-slow) var(--ease-out),
              border-color var(--duration-normal) var(--ease-out),
              box-shadow var(--duration-slow) var(--ease-out);
}

a.esn-product:hover {
  transform: translateY(-2px);
  border-color: var(--brand-200);
  box-shadow: var(--shadow-md);
}

.esn-product.is-soon { cursor: default; }

/* The monogram plate. Same --gray-900 ground and brand halo as the featured
   card, so the two dark objects in the nav are the same object. */
.esn-product__plate {
  position: relative;
  aspect-ratio: 16 / 10;
  display: grid;
  place-items: center;
  background: var(--gray-900);
  isolation: isolate;
  overflow: hidden;
}

.esn-product__plate::after {
  content: "";
  position: absolute;
  inset: -40% -30% auto auto;
  width: 80%;
  aspect-ratio: 1;
  background: radial-gradient(closest-side, rgba(232, 93, 32, 0.28), transparent 70%);
  z-index: -1;
  pointer-events: none;
}

/* The second plate is a shade lighter, which is what separates two cards that
   are otherwise the same object side by side. */
.esn-product__plate--alt { background: var(--gray-800); }

.esn-product__mono {
  font-size: var(--text-4xl);
  /* Instrument Sans is a variable font with a 400-700 weight axis (see the
     @font-face in custom-global.css). --font-extrabold is 800, which is off
     the end of it, so the browser synthesises the weight and the glyph stops
     looking like Instrument Sans. 700 is the real top of the axis. */
  font-weight: var(--font-bold);
  font-style: italic;
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  color: var(--text-inverse);
}

/* A product with a real mark: drop the black well and the brand halo, sit
   the square on the same sunken ground the rest of the card already uses.
   The file paints its own rounded plate, so this is a frame, not a second
   dark object. */
.esn-product__plate--logo {
  background: var(--bg-sunken);
}
.esn-product__plate--logo::after { content: none; }

.esn-product__logo {
  width: 52%;
  height: auto;
  aspect-ratio: 1;
  object-fit: contain;
  border-radius: 22%;
}

/* Sized down from the inline `.esn-badge` it inherits: on a card this size a
   full-width status pill competes with the monogram behind it. The label is
   "Soon" rather than "Coming soon" for the same reason, and because that is
   the word the rest of the nav already uses for an unpublished item. */
.esn-product__chip {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  z-index: 1;
  height: 18px;
  padding: 0 var(--space-2);
  letter-spacing: var(--tracking-wide);
}

.esn-product__meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3) var(--space-4);
}

.esn-product__name {
  font-size: var(--fs-card-title);
  font-weight: var(--font-bold);
  letter-spacing: var(--tracking-normal);
  color: var(--text-heading);
}

.esn-product__tag {
  font-size: var(--fs-caption);
  color: var(--text-body);
}

/* ── Featured card ───────────────────────────────────────────────────── */

.esn-feature {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--gray-900);
  color: var(--text-inverse);
  overflow: hidden;
  isolation: isolate;
  text-decoration: none;
  transition: transform var(--duration-slow) var(--ease-out),
              box-shadow var(--duration-slow) var(--ease-out);
}

.esn-feature::before {
  content: "";
  position: absolute;
  inset: -40% -30% auto auto;
  width: 80%;
  aspect-ratio: 1;
  background: radial-gradient(closest-side, rgba(232, 93, 32, 0.28), transparent 70%);
  z-index: -1;
  pointer-events: none;
}

a.esn-feature:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}

.esn-feature__title {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-card-title);
  font-weight: var(--font-bold);
  line-height: var(--lh-subhead);
  letter-spacing: var(--tracking-tight);
  color: var(--text-inverse);
}

.esn-feature__desc {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-caption);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.72);
}

/* The whole card is the link, so its CTA is a text CTA, not a second button
   inside a button. It was a bordered glass pill, which is the reference
   navbar's "ghost" control and is not a shape this site uses. Now it reads
   like `.esn-leadlink` on a dark ground and takes the same chevron sweep. */
.esn-ghost {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-caption);
  font-weight: var(--font-semibold);
  color: var(--text-inverse);
  transition: color var(--duration-normal) var(--ease-out);
}

/* brand-300 is the dark ground's brand ink (9.01:1 on --gray-900), the same
   value the dark hero gives its own eyebrow. brand-500 would be 4.36:1. */
.esn-ghost .arrow-icon { flex-shrink: 0; color: var(--brand-300); }

a.esn-feature:hover .esn-ghost { color: var(--brand-300); }

/* ── Panel footer ────────────────────────────────────────────────────── */

.esn-panefoot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
}

.esn-panefoot__meta {
  font-size: var(--fs-eyebrow);
  color: var(--text-body);
}

.esn-panefoot__meta b { color: var(--text-heading); font-weight: var(--font-semibold); }

.esn-panefoot__cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-caption);
  font-weight: var(--font-semibold);
  color: var(--text-heading);
  text-decoration: none;
  white-space: nowrap;
  transition: gap var(--duration-normal) var(--ease-out),
              color var(--duration-normal) var(--ease-out);
}

.esn-panefoot__cta:hover { color: var(--brand-700); }

/* ═════════════════════════════════════════════════════════════════════════
   THE HEADER ON A DARK GROUND

   The bar is transparent at the top of the page, so its ink belongs to
   whatever the page paints underneath it. On a dark band the ink, the caret
   and the near-black CTA pill all disappear. This block is the whole answer,
   and the rule behind it is:

     the navbar does not have a second theme. It borrows the ink the rest of
     the site already uses on a dark ground.

   The ink is the scale `section-hero.php` measured for `.es-hero--bg-dark`,
   so the bar, the panel and the hero they stand on are one piece of design
   rather than three guesses at "white-ish". The ratios below are re-measured
   against the ground the FROSTED PANE actually composites to (about #32302D
   over a --gray-900 hero), not against --gray-900 itself, because the white
   tint lifts the ground and every ratio with it:

     --text-inverse  #FFFFFF  13.13:1   headings, primary ink, hamburger
     --gray-300      #D5CFC5   8.48:1   nav labels, body copy, parked rows
     --gray-400      #B0A898   5.57:1   captions, group labels, caret
     --brand-300     #FF9D6B   6.43:1   hover and open ink, focus ring
     --brand-400     #F47340   4.61:1   the rule under an open trigger

   Every text value clears AA on the pane, on the bar (#2D2C28) and on the
   recessed plates (#23211D), which are the three grounds ink lands on. The
   4.61 belongs to a 2px decorative rule, which is held to 3:1, not 4.5:1.

   --gray-500 measures 3.43:1 here and 4.22:1 on the darkest of the three, so
   it fails on all of them. Nothing below uses it; a parked row is set one step
   down from white instead.

   NOTHING here flips to a light surface. An earlier version scoped all of it
   `:not(.is-open)`, which sent the bar back to white the moment a menu opened
   and dropped a white card onto a black hero. The panel is now the same
   smoked glass as the bar, and every element inside it is repainted.

   Which pages get the class, and when, is decided in two places and neither
   of them is here: inc/nav-theme.php renders it for the first paint, and
   assets/js/nav.js adds and removes it as `[data-nav-invert]` bands pass
   under the bar. nav.js mirrors it onto the drawer, which is a SIBLING of the
   header and so cannot inherit it.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Chrome ──────────────────────────────────────────────────────────────
   Both grounds are --gray-900 at different alphas, the same colour the hero's
   own scrim uses, so the bar densifies into the hero rather than sitting on
   top of it.

   The panel is the more opaque of the two on purpose. `backdrop-filter` on a
   descendant of an element that already has one is not reliable: the ancestor
   establishes a backdrop root and the descendant can end up sampling nothing.
   At 0.86 the card reads as smoked glass whether or not the blur resolves,
   which matters because the failure mode would otherwise be an unreadable
   near-transparent panel. */
/* The whole dark theme, as far as the CHROME is concerned, is these four
   reassignments. `.esn-stage` and `.esn-drawer` name only the semantic
   surfaces, so neither has a second rule here and the two themes cannot
   drift apart. Only the shadow is restated, because a shadow tuned for a
   pane on a white page does nothing on a black one. */
.esn-header--on-dark,
.esn-drawer--on-dark {
  --esn-glass: var(--esn-glass-dark);
  --esn-panel: var(--esn-panel-dark);
  --esn-line: var(--esn-line-dark);
  --esn-edge: var(--esn-edge-dark);
}

.esn-header--on-dark.is-scrolled,
.esn-header--on-dark.is-open { border-bottom-color: var(--esn-line-dark); }

.esn-header--on-dark .esn-stage,
.esn-drawer--on-dark {
  box-shadow: 0 24px 64px -24px rgba(0, 0, 0, 0.65),
              inset 0 1px 0 var(--esn-edge);
}

/* ── Bar ─────────────────────────────────────────────────────────────── */

/* Logo. Both files are in the document on a page that opens dark (see
   dynamic-header.php), so this is a state change, not a request. */
.esn-brand__mark--dark { display: none; }
.esn-header--on-dark .esn-brand__mark--light { display: none; }
.esn-header--on-dark .esn-brand__mark--dark { display: block; }

.esn-header--on-dark .esn-trigger,
.esn-header--on-dark .esn-navlink { color: var(--gray-300); }

.esn-header--on-dark .esn-trigger:hover,
.esn-header--on-dark .esn-navlink:hover,
.esn-header--on-dark .esn-trigger[aria-expanded="true"] { color: var(--brand-300); }

/* Lifted from 0.6: the caret is a hairline glyph, and the same opacity that
   reads as "quiet" on white reads as "missing" on --gray-900. */
.esn-header--on-dark .esn-caret {
  color: var(--gray-400);
  opacity: 0.75;
}

.esn-header--on-dark .esn-trigger::after,
.esn-header--on-dark .esn-navlink::after { background: var(--brand-400); }

/* CTA. A --gray-900 pill on a --gray-900 hero is an invisible button, so on
   dark it takes the glass treatment the dark hero already gives its own
   secondary button (`.es-hero--bg-dark .es-btn--secondary`), and the ink-wipe
   fill rises to white with the label flipping dark. The brand hairline is
   unchanged: --brand-500 reads on the white fill it draws over. */
.esn-header--on-dark .esn-cta {
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: var(--text-inverse);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
}

.esn-header--on-dark .esn-cta::before { background: var(--bg-surface); }

.esn-header--on-dark .esn-cta:hover,
.esn-header--on-dark .esn-cta:focus-visible {
  border-color: var(--bg-surface);
  color: var(--gray-900);
}

.esn-header--on-dark .esn-toggle .esn-bar { background: var(--text-inverse); }
.esn-header--on-dark .esn-toggle:hover { background: var(--esn-fill-dark); }

/* ── Panel typography ────────────────────────────────────────────────── */

.esn-header--on-dark .esn-label { color: var(--gray-400); }
.esn-header--on-dark .esn-heading { color: var(--text-inverse); }
.esn-header--on-dark .esn-sub { color: var(--gray-300); }

/* ── Panel components ────────────────────────────────────────────────── */

.esn-header--on-dark .esn-group:nth-child(even) { border-left-color: var(--esn-line-dark); }

.esn-header--on-dark .esn-service b { color: var(--text-inverse); }
.esn-header--on-dark .esn-service > span { color: var(--gray-400); }
.esn-header--on-dark a.esn-service:hover { background: var(--esn-fill-dark); }
/* --gray-500 measured 4.4:1 on the old near-black panel and 3.7:1 on this
   lighter one, so it fails AA either way. A parked row is set apart by being
   one step down from the live rows' white, not by going under the floor:
   --gray-300 is 8.6:1 here and still reads as the quieter of the two. */
.esn-header--on-dark .esn-service.is-soon b { color: var(--gray-300); }
.esn-header--on-dark .esn-service.is-soon > span { color: var(--gray-400); }

/* The watermark sits on a dark ground now, so it takes the brand ink that
   ground uses and can carry a little more opacity without shouting. */
.esn-header--on-dark .esn-service__mark { color: var(--brand-300); }
.esn-header--on-dark a.esn-service:hover .esn-service__mark { opacity: 0.24; }

/* Heavier stroke on the dark ground. The glyphs are drawn at 1.6-1.7 for a
   near-black mark on white; at 18-24% opacity in `--brand-300` on a dark
   pane the same hairline all but disappears, because what carries a stroke
   is its contrast against the ground and there is far less of it here.

   `[stroke]` rather than `path, circle`: it hits exactly the elements that
   have a stroke to widen and leaves the filled glyphs (`bag`, `users`)
   alone, which a blanket element selector would not. CSS beats the SVG
   presentation attribute, so nothing in inc/icons.php has to change and no
   other use of these icons moves. */
.esn-header--on-dark .esn-service__mark [stroke] { stroke-width: 2.4; }

.esn-header--on-dark .esn-card__title { color: var(--text-inverse); }
.esn-header--on-dark .esn-card__desc { color: var(--gray-400); }
.esn-header--on-dark a.esn-card:hover { background: var(--esn-fill-dark); }

.esn-header--on-dark .esn-card__icon {
  background: var(--esn-fill-dark);
  color: var(--gray-300);
}

.esn-header--on-dark .esn-card.is-soon .esn-card__icon,
.esn-header--on-dark .esn-card.is-soon .esn-card__title { color: var(--gray-300); }

/* Badge. The same translucent brand wash utilities.css gives `.es-eyebrow` on
   a dark section, so the two chips stay one component. */
.esn-header--on-dark .esn-badge,
.esn-drawer--on-dark .esn-badge {
  border-color: rgba(255, 157, 107, 0.28);
  background: rgba(232, 93, 32, 0.14);
  color: var(--brand-300);
}

.esn-header--on-dark .esn-stats,
.esn-header--on-dark .esn-proof {
  border-top-color: var(--esn-line-dark);
  border-bottom-color: var(--esn-line-dark);
}

.esn-header--on-dark .esn-stats > div + div::before { background: var(--esn-line-dark); }
.esn-header--on-dark .esn-stats dt { color: var(--text-inverse); }
.esn-header--on-dark .esn-stats dd { color: var(--gray-400); }
.esn-header--on-dark .esn-proof li { color: var(--gray-300); }
.esn-header--on-dark .esn-proof__icon { color: var(--brand-300); }

.esn-header--on-dark .esn-leadlink,
.esn-header--on-dark .esn-panefoot__cta { color: var(--gray-300); }
.esn-header--on-dark .esn-leadlink:hover,
.esn-header--on-dark .esn-panefoot__cta:hover { color: var(--brand-300); }
.esn-header--on-dark .esn-link__icon { color: var(--brand-300); }

.esn-header--on-dark .esn-panefoot { border-top-color: var(--esn-line-dark); }
.esn-header--on-dark .esn-panefoot__meta { color: var(--gray-400); }
.esn-header--on-dark .esn-panefoot__meta b { color: var(--text-inverse); }

/* The featured card and the product plates keep a DARK ground on a dark page.
   On the near-black panel this block used to draw they had to invert to a
   white wash, because a dark card on a dark card is one shape. The panel is
   frosted glass now, so the relationship is the right way up again: the pane
   is the light object, the plate is the well cut into it, and their contents
   were written for a dark surface to begin with. */
.esn-header--on-dark .esn-feature,
.esn-header--on-dark .esn-feature.es-bg-dark {
  background: var(--esn-sunk-dark);
  border: 1px solid var(--esn-line-dark);
}

.esn-header--on-dark .esn-product {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--esn-line-dark);
}

.esn-header--on-dark a.esn-product:hover {
  background: var(--esn-fill-dark);
  border-color: rgba(255, 157, 107, 0.32);
  box-shadow: 0 18px 40px -22px rgba(0, 0, 0, 0.7);
}

.esn-header--on-dark .esn-product__plate { background: var(--esn-sunk-dark); }
.esn-header--on-dark .esn-product__plate--alt { background: var(--esn-sunk-dark-2); }
.esn-header--on-dark .esn-product__plate--logo { background: rgba(255, 255, 255, 0.08); }
.esn-header--on-dark .esn-product__name { color: var(--text-inverse); }
.esn-header--on-dark .esn-product__tag { color: var(--gray-400); }

/* ── Mobile drawer ───────────────────────────────────────────────────────
   A sibling of the header, so nav.js mirrors the class onto it rather than it
   inheriting one. Same smoked glass as the panel. */

.esn-drawer--on-dark .esn-acc,
.esn-drawer--on-dark .esn-acclink,
.esn-drawer--on-dark .esn-acc--nested,
.esn-drawer--on-dark .esn-social { border-color: var(--esn-line-dark); }

.esn-drawer--on-dark .esn-acc > summary,
.esn-drawer--on-dark .esn-acclink,
.esn-drawer--on-dark .esn-drawer__label { color: var(--text-inverse); }

.esn-drawer--on-dark .esn-acc--nested > summary,
.esn-drawer--on-dark .esn-acc ul li a,
.esn-drawer--on-dark .esn-contact a { color: var(--gray-300); }

.esn-drawer--on-dark .esn-acc ul li a:hover,
.esn-drawer--on-dark .esn-contact a:hover { color: var(--brand-300); }

.esn-drawer--on-dark .esn-acc ul li .esn-acc-soon { color: var(--gray-400); }
.esn-drawer--on-dark .esn-acc-caret { color: var(--gray-400); }
.esn-drawer--on-dark .esn-acc[open] > summary .esn-acc-caret { color: var(--brand-300); }
.esn-drawer--on-dark .esn-acc > summary::after { background: var(--brand-400); }
.esn-drawer--on-dark .esn-acc__icon,
.esn-drawer--on-dark .esn-contact__icon { color: var(--brand-300); }
.esn-drawer--on-dark .esn-acc ul li a.esn-acc-cta { color: var(--gray-300); }
.esn-drawer--on-dark .esn-acc-cta__arrow { color: var(--gray-400); }
.esn-drawer--on-dark .esn-acc-cta:hover .esn-acc-cta__arrow { color: var(--brand-300); }
.esn-drawer--on-dark .esn-acc-mono { background: var(--esn-sunk-dark); }
.esn-drawer--on-dark .esn-acc-mono--alt { background: var(--esn-sunk-dark-2); }
.esn-drawer--on-dark .esn-acc-prod b { color: var(--text-inverse); }
.esn-drawer--on-dark .esn-acc-prod span { color: var(--gray-400); }

.esn-drawer--on-dark .esn-cta {
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: var(--text-inverse);
}

.esn-drawer--on-dark .esn-cta::before { background: var(--bg-surface); }
.esn-drawer--on-dark .esn-cta:hover { border-color: var(--bg-surface); color: var(--gray-900); }

/* --brand-500 is 4.36:1 on --gray-900, which clears WCAG 2.2's 3:1 for a focus
   indicator but sits close to the floor on the one surface where the ring
   matters most. --brand-300 is 9.01:1 and is already this ground's brand ink. */
.esn-header--on-dark :focus-visible,
.esn-drawer--on-dark :focus-visible { outline-color: var(--brand-300); }

/* ── Backdrop ────────────────────────────────────────────────────────── */

.esn-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: transparent;
  opacity: 0;
  pointer-events: none;
  -webkit-backdrop-filter: blur(8px) saturate(115%);
          backdrop-filter: blur(8px) saturate(115%);
  transition: opacity var(--duration-slow) var(--ease-out);
}

.esn-backdrop.is-on { opacity: 1; pointer-events: auto; }

/* ── Mobile drawer ───────────────────────────────────────────────────── */

.esn-drawer {
  position: fixed;
  top: calc(var(--esn-h) + var(--esn-gap));
  left: var(--esn-offset);
  right: var(--esn-offset);
  bottom: var(--esn-offset);
  z-index: 95;
  background: var(--esn-panel);
  border: 1px solid var(--esn-line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-xl), inset 0 1px 0 var(--esn-edge);
  -webkit-backdrop-filter: saturate(180%) blur(32px);
          backdrop-filter: saturate(180%) blur(32px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  transform: translateY(-12px) scale(0.985);
  transform-origin: 50% 0;
  opacity: 0;
  pointer-events: none;
  transition: transform var(--duration-slow) var(--ease-out),
              opacity var(--duration-normal) var(--ease-out);
}

.esn-drawer.is-open {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

.esn-drawer__inner {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-5) var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.esn-acc-wrap { display: flex; flex-direction: column; }

.esn-acc,
.esn-acclink { border-bottom: 1px solid var(--border-subtle); }

.esn-acc > summary,
.esn-acclink {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-1);
  font-size: var(--text-md);
  font-weight: var(--font-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--text-heading);
  text-decoration: none;
  cursor: pointer;
  list-style: none;
}

.esn-acc > summary::-webkit-details-marker { display: none; }

.esn-acc > summary::after {
  content: "";
  position: absolute;
  left: var(--space-1);
  bottom: 10px;
  width: 36px;
  height: 2px;
  border-radius: 1px;
  background: var(--brand-500);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--duration-slow) var(--ease-out);
}

.esn-acc[open] > summary::after { transform: scaleX(1); }

/* Level 2: the Shopify lifecycle groups. Smaller and indented, so the two
   levels never read as the same rank. */
.esn-acc--nested {
  border-bottom: 0;
  border-top: 1px solid var(--border-subtle);
}

.esn-acc[open] > .esn-acc--nested:first-of-type { border-top: 0; }

.esn-acc--nested > summary {
  padding: var(--space-3) var(--space-1) var(--space-3) var(--space-4);
  font-size: var(--fs-body-sm);
  font-weight: var(--font-medium);
  color: var(--text-body);
}

.esn-acc--nested > summary::after { left: var(--space-4); width: 28px; }

.esn-acc-caret {
  flex-shrink: 0;
  opacity: 0.6;
  color: var(--text-body);
  transition: transform var(--duration-slow) var(--ease-out),
              color var(--duration-normal) var(--ease-out),
              opacity var(--duration-normal) var(--ease-out);
}

.esn-acc[open] > summary .esn-acc-caret {
  transform: rotate(-180deg);
  color: var(--brand-500);
  opacity: 1;
}

/* The nested caret starts pointing right and swings down, so a group reads
   as a branch rather than a second top-level section. */
.esn-acc-caret--sub { transform: rotate(-90deg); opacity: 0.5; }
.esn-acc[open] > summary .esn-acc-caret--sub { transform: rotate(0deg); }

.esn-acc ul {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0 var(--space-1) var(--space-4);
  list-style: none;
}

.esn-acc--nested ul { padding-left: var(--space-4); padding-bottom: var(--space-3); }

.esn-acc ul li a,
.esn-acc ul li .esn-acc-soon {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 10px 0 10px var(--space-4);
  font-size: var(--fs-body-sm);
  color: var(--text-body);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-out);
}

.esn-acc ul li a:hover { color: var(--brand-700); }
.esn-acc ul li .esn-acc-soon { color: var(--text-muted); }

.esn-acc__icon {
  flex-shrink: 0;
  color: var(--brand-500);
  opacity: 0.7;
  transition: opacity var(--duration-fast) var(--ease-out);
}

.esn-acc ul li a:hover .esn-acc__icon { opacity: 1; }

/* Drawer product rows: the same monogram plate at row scale. */
.esn-acc-list--products .esn-acc-product,
.esn-acc-list--products .esn-acc-soon--product {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
}

.esn-acc-mono {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-md);
  background: var(--gray-900);
  color: var(--text-inverse);
  font-size: var(--text-lg);
  /* 700, not 800: the variable font's weight axis stops at 700. */
  font-weight: var(--font-bold);
  font-style: italic;
  line-height: 1;
  letter-spacing: var(--tracking-tight);
}

.esn-acc-mono--alt { background: var(--gray-800); }

.esn-acc-logo {
  display: block;
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-md);
  object-fit: contain;
}

.esn-acc-prod {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.esn-acc-prod b {
  font-size: var(--fs-body-sm);
  font-weight: var(--font-semibold);
  color: var(--text-heading);
}

.esn-acc-prod span {
  font-size: var(--fs-caption);
  color: var(--text-muted);
}

.esn-acc ul li a.esn-acc-cta {
  display: inline-flex;
  gap: 5px;
  padding-left: var(--space-4);
  font-size: var(--fs-caption);
  font-style: italic;
  font-weight: var(--font-medium);
  color: var(--text-heading);
  white-space: nowrap;
}

.esn-acc-cta__arrow {
  display: inline-block;
  font-style: normal;
  color: var(--text-body);
  transition: transform var(--duration-normal) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.esn-acc-cta:hover .esn-acc-cta__arrow {
  color: inherit;   /* the label is already brand-700 on hover */
  transform: translate(2px, -2px);
}

/* Drawer footer: block CTA, then the contact block the legacy drawer
   carried. Kept because it is the only place on a phone that surfaces the
   phone number, the inbox and the social profiles. */
.esn-drawer__footer {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-top: var(--space-4);
}

.esn-contact {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* A <p>, not an <h3>. The header renders before every page's <h1>, so any
   heading element in here lands out of order in the document outline and
   costs the site its Lighthouse accessibility score on all ~200 pages. The
   panels carry role="region" + aria-label instead, which is what actually
   names them for a screen reader. */
.esn-drawer__label {
  margin: 0 0 var(--space-1);
  font-size: var(--fs-small);
  font-weight: var(--font-bold);
  color: var(--text-heading);
}

.esn-contact a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-small);
  color: var(--text-body);
  text-decoration: none;
}

.esn-contact__icon { flex-shrink: 0; color: var(--brand-500); }
.esn-contact a:hover { color: var(--brand-700); }

.esn-social {
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
}

.esn-social ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

.esn-social ul li a { display: inline-flex; }
.esn-social ul li a img { width: 22px; height: 22px; }

/* ── Responsive ──────────────────────────────────────────────────────── */

/* Six nav items now, and "Case Studies" is more than twice the width of the
   "Work" it replaced, so the bar is tighter than it was at every desktop
   width. The two blocks below buy that back by trimming the trigger padding
   and the bar gaps, which are the only slack in the row: the labels, the logo
   and the CTA all have a floor. */
@media (max-width: 1199.98px) {
  .esn-inner { gap: var(--space-4); padding-left: var(--space-4); padding-right: var(--space-3); }
  .esn-trigger,
  .esn-navlink { padding: 0 10px; }
  .esn-trigger::after,
  .esn-navlink::after { left: 10px; right: 10px; }
  .esn-products { gap: var(--space-3); }
  .esn-grid { gap: var(--gap-fluid-md); }
  .esn-mega { grid-template-columns: minmax(0, 1fr) 250px; gap: var(--gap-fluid-md); }
  .esn-groups { column-gap: var(--space-5); }
  .esn-group:nth-child(even) { padding-left: var(--space-5); }
  .esn-card { grid-template-columns: 38px 1fr; gap: var(--space-3); }
  .esn-card__icon { width: 38px; height: 38px; }
}

/* The narrowest band the desktop bar has to survive: 992px with six items.
   Nothing is dropped or wrapped here, only tightened. */
@media (max-width: 1099.98px) {
  .esn-inner { gap: var(--space-3); }
  .esn-list { gap: 0; }
  .esn-trigger,
  .esn-navlink { padding: 0 var(--space-2); }
  .esn-trigger::after,
  .esn-navlink::after { left: var(--space-2); right: var(--space-2); }
  .esn-brand img { width: 128px; }
}

/* Drawer takes over at the site's existing nav breakpoint, so the header
   flips at the same width every other component on the site does. */
@media (max-width: 991.98px) {
  .esn-nav,
  .esn-actions .esn-cta { display: none; }
  .esn-actions .esn-cta--block { display: inline-flex; }
  .esn-toggle { display: inline-flex; }
  .esn-stage { display: none; }

  /* Two items left, so drop the three-column grid. With the nav and the CTA
     both display:none the grid's trailing `auto` track resolves to 0 and the
     hamburger lands a gap-width short of the padding edge; flex puts it
     exactly on it. */
  .esn-inner {
    display: flex;
    justify-content: space-between;
    padding: 0 var(--space-2) 0 var(--space-4);
    gap: var(--space-3);
  }
}

@media (max-width: 575.98px) {
  .esn-inner { padding: 0 var(--space-2) 0 var(--space-3); }
  .esn-brand img { width: 120px; }
  .esn-drawer__inner { padding: var(--space-3) var(--space-4) var(--space-5); }
}

/* ── Motion + focus ──────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .esn-header *,
  .esn-header *::before,
  .esn-header *::after,
  .esn-drawer *,
  .esn-backdrop {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }

  /* The chevron sweep is an infinite animation, so shortening its duration
     would spin it instead of stopping it. utilities.css kills it the same way
     for `.es-btn`. */
  .esn-header .arrow-icon path,
  .esn-drawer .arrow-icon path { animation: none !important; }
  .esn-header .es-btn:hover { transform: none; }
}

.esn-trigger:focus-visible,
.esn-navlink:focus-visible,
.esn-cta:focus-visible,
.esn-toggle:focus-visible,
.esn-card:focus-visible,
.esn-service:focus-visible,
.esn-feature:focus-visible,
.esn-leadlink:focus-visible,
.esn-panefoot__cta:focus-visible,
.esn-acc > summary:focus-visible,
.esn-acclink:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
