@layer dx-component-base {
/* ==========================================================================
   Component: Nav Menu

   Shared rendering primitive for navigation menus with two layout
   variants — `--horizontal` (header) and `--vertical` (footer / sidebar).
   Both variants consume the same `--dx-menu-*` typography tokens edited
   in Misc → Menu Item on the Elegance page, so changing weight/size/colour
   updates every nav at once.

   Markup contract (`components/partials/nav-menu.php` + nav-menu.js):
     <nav class="dx-nav-menu dx-nav-menu--{variant}">
       <ul class="dx-nav-menu__list">
         <li class="menu-item [menu-item-has-children]">
           <a>...</a>
           <button class="dx-nav-menu__toggle">▾</button>  // injected by JS
           <ul class="sub-menu"> ... </ul>                 // depth = 2
         </li>
       </ul>
     </nav>

   The toggle button is JS-injected because the link must stay a real
   link — clicking "About" should navigate to /about/ — while still
   giving keyboard / touch users a way to reveal children. CSS styles
   the button defensively (via `:has` is avoided to keep older Safari
   working) — if JS never runs, no caret appears and submenus stay
   collapsed (still reachable on the horizontal variant via :hover).
   ========================================================================== */

.dx-nav-menu__list,
.dx-nav-menu .sub-menu {
    list-style: none;
    margin: 0;
    padding: 0;
    z-index: 999;
}

.dx-nav-menu a {
    text-decoration: none;
    color: hsl(var(--dx-menu-color, var(--dx-text)));
    font-weight: var(--dx-menu-weight);
    font-size: var(--dx-menu-size);
    /* Empty token = inherit (line-height tracks the body Global value).
       Non-empty token overrides for every nav-menu link site-wide. Same
       pattern as Rich Text / Button. */
    line-height: var(--dx-menu-line-height, inherit);
    transition: color var(--dx-effect-transition-duration) ease;
}

.dx-nav-menu a:hover {
    /* Hover = colour change only. `--dx-menu-hover-color` is emitted
       per-mode by the Variable Manager's `dynamic_menu_hover` alias
       (wrapped here with hsl() because the stored value is an HSL
       triplet token-ref). Empty when no override is configured —
       falls back to `--dx-text` (a sensible "no visible change"
       state; users who want a real hover effect should configure
       the dynamic_menu_hover alias).
       Previously this rule also set `opacity: 0.7` as a "dim on
       hover" effect. That was a holdover from before mode-aware
       hover colours existed — the opacity competed with the colour
       change (colour brightens + opacity dims = invisible net
       change) and a workaround in inc/tokens.php was supposed to
       reset the opacity to 1 when a hover colour was configured.
       The workaround only checked the legacy typography-token
       storage path (`menu_hover_color_light/_dark`); hover colours
       configured via the newer Variable Manager dynamic_menu_hover
       alias bypassed it, leaving sites' hover effects silently
       muted. Removed entirely — hover is now a clean colour change,
       no opacity entanglement. */
    color: hsl(var(--dx-menu-hover-color, var(--dx-text)));
}

/* ── Toggle button (JS-injected) ──────────────────────────────────────────
   Sits next to a parent link as a separate click target so the link's
   own click stays a navigation. Inherits the menu colour and font size
   so it tracks any token edit. */
.dx-nav-menu__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 0;
    padding: 0.25em;
    /* Legit margin per CLAUDE.md "Spacing — use gap" — the toggle is a
       JS-injected sibling appended after the menu-item's link in normal
       inline-flow inside the <li>. The <li> is intentionally NOT flex
       (items without a toggle are link-only and rely on normal text
       flow). Small horizontal offset between the link text and this
       toggle is the right shape; converting to flex+gap would force
       layout on every menu-item including those without a toggle. */
    margin-left: 0.25em;
    /* Match the sibling link's colour so the caret reads as part of
       the link, not a dimmer affordance. We re-state the value here
       (instead of `color: inherit`) because the toggle is a SIBLING
       of the `<a>`, not a descendant — `color: inherit` would walk
       past the `.dx-nav-menu a { color: hsl(var(--dx-menu-color)) }`
       declaration (which only applies inside the link) and inherit
       the ambient section text colour instead. The SVG inside uses
       `stroke="currentColor"` so it tracks this `color` directly.
       Previously the caret was further muted by a default
       `opacity: 0.7` that made it visibly dimmer than the link —
       intended as a "secondary affordance" visual but read as a
       colour mismatch in practice. Removed: default opacity is 1
       (matches the link's default), and the hover opacity is
       picked up via the rule below so both link and caret dim
       together when the user's `--dx-menu-hover-opacity` token is
       configured for a dim-on-hover style. */
    color: hsl(var(--dx-menu-color, var(--dx-text)));
    cursor: pointer;
    font: inherit;
    line-height: 1;
    transition: transform var(--dx-effect-transition-duration) ease,
                color var(--dx-effect-transition-duration) ease;
}

/* Hover / focus — track the link's hover state. The
   `.menu-item-has-children:hover` selector triggers when the user
   hovers anywhere inside the parent menu item (including the link
   itself), so the caret stays visually tethered to its sibling
   link without the user having to aim for the tiny caret. Mirrors
   `.dx-nav-menu a:hover` — colour change only, no opacity
   declaration. See the comment on that rule for why the opacity
   was retired (the short version: it competed with the colour
   change and made configured hover colours invisible). */
.dx-nav-menu .menu-item-has-children:hover > .dx-nav-menu__toggle,
.dx-nav-menu__toggle:focus-visible {
    color: hsl(var(--dx-menu-hover-color, var(--dx-text)));
}

.dx-nav-menu__toggle svg {
    display: block;
}

.menu-item-has-children.dx-open > .dx-nav-menu__toggle {
    transform: rotate(-180deg);
}

/* ──────────────────────────────────────────────────────────────────────
   Horizontal variant (header)

   Inline row. Sub-menus render as an absolute-positioned dropdown —
   opens on parent :hover (desktop pointer) AND when the toggle button
   sets `.dx-open` (touch + keyboard).
   ────────────────────────────────────────────────────────────────────── */

.dx-nav-menu--horizontal .dx-nav-menu__list {
    display: flex;
    gap: 1.5rem;
    align-items: center;
}

.dx-nav-menu--horizontal .menu-item-has-children {
    position: relative;
}

.dx-nav-menu--horizontal .sub-menu {
    /* Hidden until parent is hovered or `.dx-open`. We use opacity +
       visibility (not display: none) so the open state can transition
       in smoothly; visibility is animated with a 0s delay on open and
       full duration on close so it doesn't kill the fade. */
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 12rem;
    /* Background, text, and border are all mode-coupled:
       - bg → `--dx-menu-surface` (dedicated dropdown background,
         configured per mode in the Variable Manager → Menu Surface
         row). Defaults to `--dx-bg` for solid contrast against the
         page content below; pre-collapse this used `--dx-surface`,
         but that token is often semi-transparent in modes layered
         over alpha overlays, which read poorly as a floating panel.
         Users can repoint each mode independently.
       - text → `--dx-text` (mode-aware, pairs with the chosen surface)
       - border → `--dx-border` (mode-aware hairline)
       For bespoke dropdown styling beyond a single background-colour
       pick, use the Custom CSS field on the Nav Menu panel. */
    background: hsl(var(--dx-menu-surface));
    color: hsl(var(--dx-text));
    border: 1px solid hsl(var(--dx-border));
    border-radius: var(--dx-effect-border-radius, 0);
    padding: 0.5rem 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(4px);
    transition: opacity var(--dx-effect-transition-duration) ease,
                transform var(--dx-effect-transition-duration) ease,
                visibility 0s linear var(--dx-effect-transition-duration);
    z-index: 100;
}

/* Desktop dropdown "open" state — gated to "drawer NOT open" so this
   rule's `transition: opacity, transform, visibility` doesn't replace
   the drawer-mode rule's `transition: max-height` when both selectors
   match. (Without the gate, this rule has higher specificity than the
   drawer rule and wins the cascade, which turned the sub-menu
   accordion in drawer mode into an unanimated snap-open — the same
   element is supposed to animate via max-height when the parent is
   `.dx-nav-menu--open`, and via opacity/transform when it isn't.)
   The `:not(.dx-nav-menu--open)` qualifier on the root makes "desktop
   dropdown" and "drawer accordion" mutually exclusive at the CSS
   level. */
.dx-nav-menu--horizontal:not(.dx-nav-menu--open) .menu-item-has-children:hover > .sub-menu,
.dx-nav-menu--horizontal:not(.dx-nav-menu--open) .menu-item-has-children:focus-within > .sub-menu,
.dx-nav-menu--horizontal:not(.dx-nav-menu--open) .menu-item-has-children.dx-open > .sub-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    /* On open: visibility flips immediately (delay 0) so the fade is
       visible from the start. */
    transition: opacity var(--dx-effect-transition-duration) ease,
                transform var(--dx-effect-transition-duration) ease,
                visibility 0s linear 0s;
}

.dx-nav-menu--horizontal .sub-menu li {
    display: block;
}

.dx-nav-menu--horizontal .sub-menu a {
    display: block;
    padding: 0.5rem 1rem;
    white-space: nowrap;
    /* Link colour inherits the panel's `color: hsl(var(--dx-text))`
       set on `.sub-menu` above — mode-aware, pairs correctly with the
       mode-aware bg-2 fallback. The base `.dx-nav-menu a` rule's
       --dx-menu-color override stays in effect when explicitly set. */
}

/* ──────────────────────────────────────────────────────────────────────
   Mobile collapse (horizontal variant only)

   Below `48rem` of available section width, the horizontal variant
   transforms into a hamburger button + full-viewport drawer:
     - The inline `<ul>` is hidden.
     - A JS-injected `.dx-nav-menu__hamburger` (three lines) shows in
       its place.
     - Tapping the hamburger sets `.dx-nav-menu--open` on the nav,
       which fades in the drawer (same `<ul>`, repositioned as a
       fixed full-viewport panel with items stacked vertically).
     - The hamburger morphs to an X — three CSS-animated `<span>`
       bars where top/bottom rotate ±45° onto the centre line and the
       middle bar fades out, all on the same transition duration as
       the drawer fade. The button z-indexes above the drawer so it
       stays the close affordance.
     - Sub-menus inside the drawer behave as accordions — same
       `max-height` mechanism the `--vertical` variant uses. The JS
       (`isAccordionMode` in nav-menu.js) treats "horizontal + open
       drawer" identically to "vertical."

   Container-query, not viewport-query: the nav menu collapses based
   on the actual width of its enclosing `dx-section`, not the viewport.
   For full-width headers (the dominant case) the two are equivalent;
   for a hypothetical horizontal nav inside a narrow column on a wide
   viewport, container is the only one that gives the correct result.

   Graceful no-op: if a horizontal nav is rendered outside any
   `dx-section` ancestor (e.g. the /elegance Nav Menu showcase
   preview), the container query never fires and the menu stays in
   its expanded form — which is what the preview should show anyway.

   Tokens reused (no new ones):
     - `--dx-menu-surface`  → drawer background (existing).
     - `--dx-text`          → drawer text (existing, mode-aware).
     - `--dx-effect-transition-duration` → fade duration (existing).
     - `--dx-space-*`       → padding + inter-item gap (existing).
   ────────────────────────────────────────────────────────────────────── */

/* Hamburger button — JS-injected as the first child of every
   `.dx-nav-menu--horizontal`. Hidden at all widths by default; the
   container query below reveals it when the section narrows. Contains
   three `<span>` bars (see HAMBURGER_BARS in nav-menu.js) that
   animate between the hamburger (≡) and close (X) states via CSS
   transforms on the parent's `.dx-nav-menu--open` class.

   Colour: `color: inherit` pulls down the menu colour
   (`--dx-menu-color`) the nav menu set on the `<nav>` element. The
   bars themselves use `background: currentColor` so colour changes
   on the button flow through automatically. Hover swaps to
   `--dx-menu-hover-color` (same token the nav menu links use), so
   the icon hover state tracks the surrounding link hover state for
   free — no separate token surface for "hamburger hover colour."

   Sizing: the button is 2.5rem × 2.5rem (touch-target-friendly).
   The bars sit absolutely-positioned at the centre and use
   transform to draw their three-line geometry. */
.dx-nav-menu__hamburger {
    display: none;
    position: relative;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    background: transparent;
    border: 0;
    color: inherit;
    cursor: pointer;
    transition: color var(--dx-effect-transition-duration) ease;
}

.dx-nav-menu__hamburger:hover,
.dx-nav-menu__hamburger:focus-visible {
    color: hsl(var(--dx-menu-hover-color, var(--dx-menu-color, var(--dx-text))));
}

/* ── Bar stack — three lines that morph between ≡ and X ──────────
   Each bar is a 2px-tall pill positioned absolutely at the button
   centre, then offset vertically via `transform: translate(-50%, …)`.
   Variables expose the geometry so the bars stay in one place:

     --bar-width   length of each bar
     --bar-height  thickness
     --bar-spread  vertical distance from centre to top/bottom bar

   On `.dx-nav-menu--open`:
     - top bar collapses onto centre line and rotates +45°
     - middle bar fades to opacity 0 (its width is part of the X
       anyway — fading is enough; no need to also collapse it)
     - bottom bar collapses onto centre line and rotates -45°

   The three transforms compose with a single `translate(-50%, …)`
   for centering, so we never see the "double-transform" jitter that
   happens when transitioning between two `transform` declarations
   with different operation orders. */
.dx-nav-menu__hamburger-bar {
    --bar-width: 1.25rem;
    --bar-height: 2px;
    --bar-spread: 6px;

    position: absolute;
    left: 50%;
    top: 50%;
    width: var(--bar-width);
    height: var(--bar-height);
    background: currentColor;
    border-radius: calc(var(--bar-height) / 2);
    transform-origin: center;
    transition:
        transform var(--dx-effect-transition-duration) ease,
        opacity var(--dx-effect-transition-duration) ease;
}

.dx-nav-menu__hamburger-bar:nth-child(1) {
    transform: translate(-50%, calc(-50% - var(--bar-spread)));
}
.dx-nav-menu__hamburger-bar:nth-child(2) {
    transform: translate(-50%, -50%);
}
.dx-nav-menu__hamburger-bar:nth-child(3) {
    transform: translate(-50%, calc(-50% + var(--bar-spread)));
}

/* Open state — three bars collapse to an X.
   Top bar → centre + rotate(45deg).
   Middle bar → fade out.
   Bottom bar → centre + rotate(-45deg). */
.dx-nav-menu--open .dx-nav-menu__hamburger-bar:nth-child(1) {
    transform: translate(-50%, -50%) rotate(45deg);
}
.dx-nav-menu--open .dx-nav-menu__hamburger-bar:nth-child(2) {
    transform: translate(-50%, -50%);
    opacity: 0;
}
.dx-nav-menu--open .dx-nav-menu__hamburger-bar:nth-child(3) {
    transform: translate(-50%, -50%) rotate(-45deg);
}

@container dx-section (max-width: 48rem) {
    /* Reveal the hamburger. position+z-index lift it above the
       drawer panel so it stays the close affordance when open.
       Closed state: `position: relative` so it sits at its natural
       DOM position inside the header (alongside the logo / CTA /
       whatever else the section composes). Open state: see the
       override directly below. */
    .dx-nav-menu--horizontal .dx-nav-menu__hamburger {
        display: inline-flex;
        position: relative;
        z-index: 1001;
    }

    /* Open state — pin to viewport top-right with a fixed inset.
       Without this, the X stayed wherever the section layout
       originally placed the hamburger (often centred or left-of-
       centre when the nav menu sat in a middle column), making
       "close" non-obvious on a full-viewport drawer. Anchoring to
       the top-right corner with a comfortable inset matches the
       conventional off-canvas-close affordance position users expect.
       `position: fixed` is relative to the viewport (same coordinate
       system as the drawer's `inset: 0`), so the X tracks the
       viewport regardless of scroll position or section layout. */
    .dx-nav-menu--horizontal.dx-nav-menu--open .dx-nav-menu__hamburger {
        position: fixed;
        top: 1.25rem;
        right: 1.25rem;
    }

    /* The inline list flips into "drawer mode" at this width — pre-
       baked as fixed full-viewport, opacity:0 + visibility:hidden so
       it's offstage until `.dx-nav-menu--open` flips them. Same
       opacity+visibility+delay trick the desktop dropdown uses (see
       horizontal sub-menu rules above) so the fade-out completes
       before visibility hides the element from interaction. */
    .dx-nav-menu--horizontal .dx-nav-menu__list {
        display: flex;
        flex-direction: column;
        gap: var(--dx-space-s);
        align-items: stretch;

        position: fixed;
        inset: 0;
        z-index: 1000;

        background: hsl(var(--dx-menu-surface));
        color: hsl(var(--dx-text));
        /* Top padding leaves room for the hamburger (which floats above
           via z-index) so menu items don't slide under it. */
        padding: calc(var(--dx-space-2xl) + 1rem) var(--dx-space-l) var(--dx-space-l);
        overflow-y: auto;

        opacity: 0;
        visibility: hidden;
        transition: opacity var(--dx-effect-transition-duration) ease,
                    visibility 0s linear var(--dx-effect-transition-duration);
    }

    .dx-nav-menu--horizontal.dx-nav-menu--open .dx-nav-menu__list {
        opacity: 1;
        visibility: visible;
        transition: opacity var(--dx-effect-transition-duration) ease,
                    visibility 0s linear 0s;
    }

    /* ── Drawer alignment — 9 named positions ──────────────────────
       The `<ul>` is `position: fixed; inset: 0` (full viewport) with
       `display: flex; flex-direction: column`. The 9 positions map
       to flex axis controls:
         - `align-items` controls the HORIZONTAL position (cross axis
            in a column flex).
         - `justify-content` controls the VERTICAL position (main
            axis).
       Items render at content width (intrinsic) when align-items is
       anything other than `stretch`, so they actually sit at the
       chosen edge — picking "Right" pushes the menu items to the
       right edge of the drawer, etc. Default site-wide value is
       'left' (set in inc/tokens-defaults.php), which lands here as
       `.dx-nav-menu--drawer-align-left`: vertically centred + left
       edge. Users who want the old top-stretched layout can pick
       'top-left' or override with Custom CSS.
       Class lands on the <nav> root (the partial emits it
       unconditionally), but these rules are scoped to
       `.dx-nav-menu--horizontal.dx-nav-menu--open` so the vertical
       variant + closed-drawer state ignore them. */
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-center .dx-nav-menu__list {
        align-items: center;
        justify-content: center;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-top .dx-nav-menu__list {
        align-items: center;
        justify-content: flex-start;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-top-left .dx-nav-menu__list {
        align-items: flex-start;
        justify-content: flex-start;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-top-right .dx-nav-menu__list {
        align-items: flex-end;
        justify-content: flex-start;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-left .dx-nav-menu__list {
        align-items: flex-start;
        justify-content: center;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-right .dx-nav-menu__list {
        align-items: flex-end;
        justify-content: center;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-bottom .dx-nav-menu__list {
        align-items: center;
        justify-content: flex-end;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-bottom-left .dx-nav-menu__list {
        align-items: flex-start;
        justify-content: flex-end;
    }
    .dx-nav-menu--horizontal.dx-nav-menu--open.dx-nav-menu--drawer-align-bottom-right .dx-nav-menu__list {
        align-items: flex-end;
        justify-content: flex-end;
    }

    /* Top-level items as stacked rows inside the drawer */
    .dx-nav-menu--horizontal .menu-item {
        display: block;
        position: static;
    }

    /* Sub-menu accordion inside the drawer — same shape as the
       --vertical variant. nav-menu.js's `isAccordionMode` check
       treats this state identically and drives the same JS-set
       max-height transition. */
    .dx-nav-menu--horizontal .sub-menu {
        overflow: hidden;
        max-height: 0;
        padding-left: var(--dx-space-m);
        display: flex;
        flex-direction: column;
        gap: var(--dx-space-s);

        /* Reset the horizontal-variant dropdown chrome that's
           inherited from rules above: no absolute positioning, no
           background panel, no border / shadow / radius — the drawer
           IS the panel surface, sub-menus are just indented rows. */
        position: static;
        background: transparent;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        padding-block: 0;
        padding-right: 0;
        opacity: 1;
        visibility: visible;
        transform: none;
        min-width: 0;
        transition: max-height var(--dx-effect-transition-duration) ease;
    }

    /* The horizontal variant's hover-opens-dropdown rule fights the
       accordion. Disable it inside the drawer — sub-menus only open
       via the existing JS caret toggle (which is the same affordance
       the vertical variant already uses). */
    .dx-nav-menu--horizontal .menu-item-has-children:hover > .sub-menu,
    .dx-nav-menu--horizontal .menu-item-has-children:focus-within > .sub-menu {
        opacity: 1;
        visibility: visible;
        transform: none;
    }

    /* Drawer-mode sub-menu link styling — no per-item padding (the
       horizontal dropdown applied 0.5rem 1rem); white-space wraps
       naturally inside the drawer. */
    .dx-nav-menu--horizontal .sub-menu a {
        display: inline-block;
        padding: 0;
        white-space: normal;
    }
}

/* ──────────────────────────────────────────────────────────────────────
   Portaled drawer — when `nav-menu.js` opens the mobile drawer, it
   moves the entire `<nav class="dx-nav-menu dx-nav-menu--horizontal">`
   to a `<div id="dx-nav-portal">` mounted at <body> and adds
   `body.dx-nav-drawer-open`. This block provides every open-state
   rule the drawer needs at the body-level — NOT inside the section's
   `@container dx-section` query — because after portal the nav has
   no `.dx-section` ancestor and the @container rules above no longer
   match.

   The split:
     - The @container block above continues to pre-stage the drawer
       in a NARROW section (hamburger reveal, base flex column +
       fixed inset 0 + invisible). That's the "before the user clicks"
       state.
     - This block takes over once the user clicks — the nav lives at
       body level, body has the drawer-open class, and these rules
       reveal the drawer + position the close (X) button. Independent
       of section width because the open intent was an explicit user
       action, not a responsive-collapse decision.

   Why the portal is needed: `.dx-section` has `container-type:
   inline-size`, which implies `contain: layout`. Layout containment
   makes the section the containing block for any `position: fixed`
   descendant — so the drawer's `inset: 0` covered the section's
   bounds, not the viewport. Moving the nav out of the section is
   the only way for `position: fixed` to resolve against the viewport
   as intended. See `nav-menu.js`'s `portalNavToBody` for the JS side.
   ────────────────────────────────────────────────────────────────────── */
body.dx-nav-drawer-open #dx-nav-portal .dx-nav-menu--horizontal {
    /* Naked context — no .dx-section parent, no padding from a header
       container. The portal is a transparent wrapper; the nav drives
       its own layout from here. */
    display: block;
    position: static;
}

/* Hamburger: revealed + pinned to viewport top-right as the close (X).
   The CSS-animated three-bar morph (the original `.dx-nav-menu--open`
   rules on `.dx-nav-menu__hamburger-bar`) continues to work — those
   rules live outside @container and the morph triggers on the
   `.dx-nav-menu--open` class which the portaled nav still carries. */
body.dx-nav-drawer-open .dx-nav-menu--horizontal .dx-nav-menu__hamburger {
    display: inline-flex;
    position: fixed;
    top: 1.25rem;
    right: 1.25rem;
    z-index: 1001;
}

/* The list IS the drawer surface — fullscreen, scrollable, painted
   with the mode-aware menu surface token. `inset: 0` now resolves to
   the viewport (the whole point of the portal). */
body.dx-nav-drawer-open .dx-nav-menu--horizontal .dx-nav-menu__list {
    display: flex;
    flex-direction: column;
    gap: var(--dx-space-s);
    align-items: stretch;

    position: fixed;
    inset: 0;
    z-index: 1000;

    background: hsl(var(--dx-menu-surface));
    color: hsl(var(--dx-text));
    /* Top padding leaves room for the X (which floats above via
       z-index) so menu items don't slide under it. Same value as the
       @container block — keep in sync if tuned. */
    padding: calc(var(--dx-space-2xl) + 1rem) var(--dx-space-l) var(--dx-space-l);
    overflow-y: auto;
    opacity: 1;
    visibility: visible;
}

/* ── Drawer alignment — portal mode (mirror of the @container block) ──
   Once the JS portals the nav to <body>, the nav has no `.dx-section`
   ancestor so the @container rules above stop matching. The 9
   alignment classes lose their effect at the exact moment they're
   supposed to apply (the drawer-open state). Duplicate the rules
   here, scoped to `body.dx-nav-drawer-open`, so they actually fire
   on the visible drawer.
   Keep these in sync with the @container variants above — same flex
   axis mappings, same content-width behaviour. */
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-center .dx-nav-menu__list {
    align-items: center;
    justify-content: center;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-top .dx-nav-menu__list {
    align-items: center;
    justify-content: flex-start;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-top-left .dx-nav-menu__list {
    align-items: flex-start;
    justify-content: flex-start;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-top-right .dx-nav-menu__list {
    align-items: flex-end;
    justify-content: flex-start;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-left .dx-nav-menu__list {
    align-items: flex-start;
    justify-content: center;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-right .dx-nav-menu__list {
    align-items: flex-end;
    justify-content: center;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-bottom .dx-nav-menu__list {
    align-items: center;
    justify-content: flex-end;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-bottom-left .dx-nav-menu__list {
    align-items: flex-start;
    justify-content: flex-end;
}
body.dx-nav-drawer-open .dx-nav-menu--horizontal.dx-nav-menu--drawer-align-bottom-right .dx-nav-menu__list {
    align-items: flex-end;
    justify-content: flex-end;
}

/* Top-level items as stacked rows */
body.dx-nav-drawer-open .dx-nav-menu--horizontal .menu-item {
    display: block;
    position: static;
}

/* Sub-menu accordion inside the drawer — same shape as the @container
   block above. Inline `max-height` is driven by `nav-menu.js`'s
   `openVertical` / `closeVertical` helpers; this rule provides the
   closed `max-height: 0` floor and the shared transition. */
body.dx-nav-drawer-open .dx-nav-menu--horizontal .sub-menu {
    overflow: hidden;
    max-height: 0;
    padding-left: var(--dx-space-m);
    display: flex;
    flex-direction: column;
    gap: var(--dx-space-s);
    position: static;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding-block: 0;
    padding-right: 0;
    opacity: 1;
    visibility: visible;
    transform: none;
    min-width: 0;
    transition: max-height var(--dx-effect-transition-duration) ease;
}

/* Hover-opens-dropdown rule from the desktop variant would fight
   the accordion if it leaked through. Suppress it inside the drawer
   context — sub-menus only open via the JS caret toggle. */
body.dx-nav-drawer-open .dx-nav-menu--horizontal .menu-item-has-children:hover > .sub-menu,
body.dx-nav-drawer-open .dx-nav-menu--horizontal .menu-item-has-children:focus-within > .sub-menu {
    opacity: 1;
    visibility: visible;
    transform: none;
}

/* Drawer-mode sub-menu link styling */
body.dx-nav-drawer-open .dx-nav-menu--horizontal .sub-menu a {
    display: inline-block;
    padding: 0;
    white-space: normal;
}

/* ──────────────────────────────────────────────────────────────────────
   Vertical variant (footer / sidebar)

   Stacked column with:
     - Underline-on-hover via ::before (animates 0 → 100% width)
     - Caret toggle button (JS-injected) — clicking the parent link
       still navigates; only the button toggles
     - Sub-menu accordion via max-height transition; toggled by the
       `.dx-open` class added by `nav-menu.js`
   ────────────────────────────────────────────────────────────────────── */

.dx-nav-menu--vertical .dx-nav-menu__list {
    display: flex;
    flex-direction: column;
    gap: var(--dx-space-s);
}

.dx-nav-menu--vertical .menu-item {
    position: relative;
}

.dx-nav-menu--vertical > .dx-nav-menu__list > .menu-item > a {
    display: inline-block;
    padding-block: 0.15em;
}

/* Sub-menu accordion. Closed by default (max-height: 0); the open
   value is set inline by `nav-menu.js` to the element's real
   `scrollHeight`, so the animation traverses the actual content
   height — not a generic 1000px upper bound that makes the open
   transition appear instant on short submenus.

   Inter-item spacing lives on the parent via `gap` — same model as
   the top-level vertical list above. CLAUDE.md "Spacing — use gap". */
.dx-nav-menu--vertical .sub-menu {
    overflow: hidden;
    max-height: 0;
    padding-left: var(--dx-space-m);
    display: flex;
    flex-direction: column;
    gap: var(--dx-space-s);
    transition: max-height var(--dx-effect-transition-duration) ease;
}
