/* ==========================================================================
   DX Default Styles

   Base layout and section mode styles for the Elegance framework.
   Color tokens, typography, and spacing values are rendered as inline CSS
   from the database via dx_output_token_styles() (inc/tokens.php).
   ========================================================================== */

/* ---------- Global font smoothing ----------
   Switch from the OS-default subpixel AA to grayscale AA. Matches what type
   designers see in design tools (Figma/Sketch) and what every modern dark-
   mode design system ships (Linear, Vercel, Stripe, Tailwind's Preflight).
   Subpixel rendering visibly over-thickens light text on dark backgrounds —
   this fixes that across the site, the front-editor, and the Elegance chrome
   in one place. Applied on `html` (not `body`) so form inputs inherit too. */
html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Smooth in-page navigation when a link with a `#id` href jumps to
     a section. Instant snap-jumps feel abrupt — especially in long
     pages where the user loses track of where they came from. CSS-
     only smooth scroll respects the user's prefers-reduced-motion
     accessibility setting automatically (browsers fall back to
     `auto` when the OS-level Reduce Motion is on). Site-wide so
     it applies to every anchor in the theme — Custom CSS IDs on
     sections / containers / cores, table-of-contents links, "back
     to top" affordances, etc. */
  scroll-behavior: smooth;
}

/* ---------- Root aliases ----------
   Mode classes (below) redefine these to mode-appropriate values. The
   root aliases exist so consumers of the mode tokens (e.g. the primary
   button color rule in core.css) still resolve to sensible values when
   rendered outside a `.dx-mode-*` scope — for example the sidebar
   "Save Changes" button in the showcase. Without these, `--dx-primary`
   would be undefined outside a mode scope and any property consuming it
   via a bare `var(--dx-primary)` (component CSS no longer carries inline
   fallbacks — see Token Fallbacks note in style.css) would resolve to
   `unset`, breaking the rule. */
:root {
  /* Section-mode aliases consumed by component CSS (`hsl(var(--dx-bg))`
       etc.). The dynamic-* vars are themselves mode-aware — emitted
       per `.dx-mode-N` rule by `dx_build_dynamic_emission_css()`. At
       `:root` level (no mode scope) they resolve to mode 1's values
       (the emitter scopes mode 1 to both `:root` and `.dx-mode-1`).
       Sections referencing a mode ID not configured on this site
       inherit the `:root` (mode 1) values via natural cascade —
       import/export portability is free.

       Every mode-aware role has a `--dx-color-dynamic-*` source (from
       Dynamic Lab) and a `--dx-*` role var (consumed by component CSS).
       The role-var aliasing here is the only place the binding lives.
       Adding a Dynamic alias = one new row here + one in
       dx_get_dynamic_alias_keys(). */
  --dx-bg: var(--dx-color-dynamic-bg);
  --dx-surface: var(--dx-color-dynamic-surface);
  --dx-heading: var(--dx-color-dynamic-heading);
  --dx-text: var(--dx-color-dynamic-text);
  --dx-eyebrow: var(--dx-color-dynamic-eyebrow);
  --dx-border: var(--dx-color-dynamic-border);
  --dx-menu-color: var(--dx-color-dynamic-menu);
  --dx-menu-hover-color: var(--dx-color-dynamic-menu-hover);
  --dx-menu-surface: var(--dx-color-dynamic-menu-surface);
  --dx-link-color: var(--dx-color-dynamic-link);
  --dx-link-hover-color: var(--dx-color-dynamic-link-hover);
  --dx-button: var(--dx-color-dynamic-button);
  --dx-button-hover: var(--dx-color-dynamic-button-hover);
  --dx-svg: var(--dx-color-dynamic-svg);
  --dx-svg-hover: var(--dx-color-dynamic-svg-hover);
  --dx-primary: var(--dx-color-primary);
}

/* ---------- Dynamic colour resolution ----------
   The Dynamic colour layer is the user-facing surface of the colour
   system. Every per-component colour picker lists six semantic roles
   (background, surface, heading, text, border, eyebrow) as `dynamic-*`
   aliases. At authoring time the user picks one; at render time the
   alias resolves to the chosen underlying token (a `neutral_*` step,
   an alpha overlay, a primary-stack shade, a primitive, or a raw
   custom value) based on the active `.dx-mode-N` scope, where each
   Dynamic ref stores an independent choice per mode.

   Convention: dynamic-* values are HSL TRIPLETS (or triplets-with-alpha
   for alpha overlays, or `var(--dx-color-*)` references, or `custom:<hsl>`
   payloads). Consumers wrap with `hsl(var(...))` exactly like every
   other colour token — uniform pattern, no special-case handling at
   the call site.

   Emission lives in `dx_build_dynamic_emission_css()` (inc/tokens.php),
   which writes per-mode `.dx-mode-N` mapping blocks. Mode-awareness
   lives entirely at the Dynamic layer. */

/* ---------- Section Modes ----------
   Every configured mode emits its own `.dx-mode-N` rule below, mapping
   the Dynamic alias vars (`--dx-color-dynamic-*`) into the section
   role vars (`--dx-bg`, `--dx-surface`, etc.) so component CSS can
   keep consuming the role vars uniformly. The actual per-mode COLOUR
   values are written by `dx_build_dynamic_emission_css()` —
   `<style id="dx-tokens">` overrides these aliases inside each
   `.dx-mode-N` block.

   The selector list below is intentionally permissive (`[class*="dx-mode-"]`)
   so any current OR future mode picks up the role aliasing automatically.
   No need to enumerate `.dx-mode-1, .dx-mode-2, …` here — the dynamic
   emission already scopes its overrides per mode, and the role aliases
   are mode-agnostic (they just point at the active dynamic-* vars). */
[class*="dx-mode-"] {
  --dx-bg: var(--dx-color-dynamic-bg);
  --dx-surface: var(--dx-color-dynamic-surface);
  --dx-heading: var(--dx-color-dynamic-heading);
  --dx-text: var(--dx-color-dynamic-text);
  --dx-eyebrow: var(--dx-color-dynamic-eyebrow);
  --dx-border: var(--dx-color-dynamic-border);
  --dx-menu-color: var(--dx-color-dynamic-menu);
  --dx-menu-hover-color: var(--dx-color-dynamic-menu-hover);
  --dx-menu-surface: var(--dx-color-dynamic-menu-surface);
  --dx-link-color: var(--dx-color-dynamic-link);
  --dx-link-hover-color: var(--dx-color-dynamic-link-hover);
  --dx-button: var(--dx-color-dynamic-button);
  --dx-button-hover: var(--dx-color-dynamic-button-hover);
  --dx-svg: var(--dx-color-dynamic-svg);
  --dx-svg-hover: var(--dx-color-dynamic-svg-hover);
  --dx-primary: var(--dx-color-primary);

  background-color: hsl(var(--dx-bg));
  color: hsl(var(--dx-text));
}

/* ---------- Layout ---------- */

.dx-section {
  width: 100%;
  padding-inline: var(--dx-spacing-gutter);
  /* Default block padding now reads a stored token (was a derived
       calc(var(--dx-spacing-gutter) * 2)). Edit it on the Section panel
       under the Elegance settings — every section that doesn't override
       via its `padding_block` field inherits this value. */
  padding-block: var(--dx-spacing-padding-block-default);
  /* CONTAINER QUERY ROOT lives in `components/section.css` —
     `container-type: inline-size` + `container-name: dx-section`. Don't
     duplicate it here. That property is what lets the typography token
     clamps (built by `dx_build_fluid_clamp()` in inc/tokens.php) use
     `cqi` as their fluid unit and resolve against the section's width
     rather than the browser viewport — the basis of the preview-pane
     resize honouring fluid type. */
}

.dx-container {
  /* `width: 100%` is required for flex contexts. In a normal block
       parent, max-width + auto margins is enough to fill up to the cap.
       But when a `.dx-section` becomes a flex container (e.g. `.dx-hero`
       uses flex + justify-content to vertically + horizontally center
       its child), `.dx-container` becomes a *flex item* and would
       otherwise shrink to its content's intrinsic width — collapsing a
       1200px container to whatever the longest line of text needs.
       Setting width: 100% restores block behaviour inside flex parents
       without affecting non-flex sections. */
  width: 100%;
  /* Default container width = `medium`. Sections override per-instance
       via the `.dx-section--container-{small,medium,large,full}` modifier
       classes below — each one scopes a max-width on the `> .dx-container`
       descendant. The wrapper element stays identical across every section;
       only the cap changes. */
  max-width: var(--dx-container-width-default);
  /* margin: 0 auto — horizontal centering for a capped-width container.
       Legit per CLAUDE.md (no parent gap can express centering). */
  margin: 0 auto;
}

/* ── Section modifiers — vertical padding + min-height ────────────────────
   Live unlayered alongside `.dx-section` so they win over the base rule
   by source order. If they were inside `@layer dx-component-base` (where
   feature.css / hero.css live) the unlayered base would always beat them
   — that's exactly the bug the per-component duplicates ran into.
   Defined ONCE here on `.dx-section` so every section type (Hero, Feature,
   any future section) inherits them automatically — no per-component
   duplication. */

/* Block padding modifiers — renamed from `--padding-{value}` to
   `--padding-block-{value}` since we now ALSO have inline-padding
   modifiers (sibling rules below). The value vocabulary is identical
   to the t-shirt scale plus `none`. Section renderers (hero.php,
   feature.php, footer.php) emit the new class names; legacy stored
   `padding` field values are read via `padding_block ?? padding` so
   pages saved before the rename still render correctly. */
/* `default` = inherit the Layout page's "Default Block Padding" global. An
   explicit, higher-specificity class (beats the `.dx-section` base rule) so a
   fresh/unset section reliably picks up the site-wide default rather than
   collapsing to zero. Edit the value on Elegance → Layout. */
.dx-section--padding-block-default {
  padding-block: var(--dx-spacing-padding-block-default);
}
.dx-section--padding-block-none {
  padding-block: 0;
}
.dx-section--padding-block-2xs {
  padding-block: var(--dx-space-2xs);
}
.dx-section--padding-block-xs {
  padding-block: var(--dx-space-xs);
}
.dx-section--padding-block-s {
  padding-block: var(--dx-space-s);
}
.dx-section--padding-block-m {
  padding-block: var(--dx-space-m);
}
.dx-section--padding-block-l {
  padding-block: var(--dx-space-l);
}
.dx-section--padding-block-xl {
  padding-block: var(--dx-space-xl);
}
.dx-section--padding-block-2xl {
  padding-block: var(--dx-space-2xl);
}
.dx-section--padding-block-3xl {
  padding-block: var(--dx-space-3xl);
}
.dx-section--padding-block-4xl {
  padding-block: var(--dx-space-4xl);
}
.dx-section--padding-block-5xl {
  padding-block: var(--dx-space-5xl);
}

/* Inline padding modifiers — same scale, override the section's default
   `padding-inline: var(--dx-spacing-gutter)`. Useful for hero sections
   that need full-bleed content, or sections that want generous side
   padding for a narrower visual feel without changing the container width. */
/* `default` = inherit the Layout page's "Section Inline Padding" global
   (the gutter). Explicit class — same rationale as padding-block-default. */
.dx-section--padding-inline-default {
  padding-inline: var(--dx-spacing-gutter);
}
.dx-section--padding-inline-none {
  padding-inline: 0;
}
.dx-section--padding-inline-2xs {
  padding-inline: var(--dx-space-2xs);
}
.dx-section--padding-inline-xs {
  padding-inline: var(--dx-space-xs);
}
.dx-section--padding-inline-s {
  padding-inline: var(--dx-space-s);
}
.dx-section--padding-inline-m {
  padding-inline: var(--dx-space-m);
}
.dx-section--padding-inline-l {
  padding-inline: var(--dx-space-l);
}
.dx-section--padding-inline-xl {
  padding-inline: var(--dx-space-xl);
}
.dx-section--padding-inline-2xl {
  padding-inline: var(--dx-space-2xl);
}
.dx-section--padding-inline-3xl {
  padding-inline: var(--dx-space-3xl);
}
.dx-section--padding-inline-4xl {
  padding-inline: var(--dx-space-4xl);
}
.dx-section--padding-inline-5xl {
  padding-inline: var(--dx-space-5xl);
}

/* Container-width modifiers.
   ────────────────────────────
   Two parallel selector forms, both producing the same `max-width`
   result — one for each container architecture surface:

   - DIRECT modifier on the container (`.dx-container.dx-container--{N}`)
     — used by the new container-architecture section renderer
     (`components/section.php` after Phase 2). Each container in a
     section can carry its own width independently, so the selector
     binds the width to THE container element directly. Authored
     markup: `<div class="dx-container dx-container--large">…</div>`.

   - SECTION-level modifier on the section's direct container
     (`.dx-section--container-{N} > .dx-container`) — used by header
     and footer template parts which retain the single-container
     shape with section-level `container_width`. `dx_render_section_
     classes()` emits the `.dx-section--container-{N}` class on the
     section element; the inner `.dx-container` gets its width via
     this descendant selector. Authored markup:
     `<footer class="dx-footer dx-section--container-large"><div class="dx-container">…</div></footer>`.

   Both forms coexist — they target different shapes, so there's no
   specificity conflict. The site-wide `.dx-container` default
   (`max-width: var(--dx-container-width-default)` above) is the
   floor when neither modifier is present.

   `full` sets `max-width: none` rather than dropping the
   `.dx-container` element entirely, so the markup contract stays
   identical across every section type. */
.dx-container.dx-container--xs,
.dx-section--container-xs > .dx-container {
  max-width: var(--dx-container-width-xs);
}
.dx-container.dx-container--small,
.dx-section--container-small > .dx-container {
  max-width: var(--dx-container-width-small);
}
.dx-container.dx-container--medium,
.dx-section--container-medium > .dx-container {
  max-width: var(--dx-container-width-medium);
}
.dx-container.dx-container--large,
.dx-section--container-large > .dx-container {
  max-width: var(--dx-container-width-large);
}
.dx-container.dx-container--full,
.dx-section--container-full > .dx-container {
  max-width: none;
}

/* (A `--dx-current-container-width` variable used to be set per
   container class here for the Media Bleed math to consume. Removed
   alongside Bleed itself. If a future feature needs to know the
   section's effective max-width at descendant scope, restore this
   block.) */

/* ── Vertical alignment of section content ──────────────────────────────
   Turns the section into a flex column so its single child (.dx-container)
   stacks against the chosen edge. Only meaningful when the section has a
   min-height that gives the content vertical room; at the natural height
   the container fills the section and the alignment is a no-op visually.
   Empty modifier = inherit (no flex layout, content sits at the top by
   normal block flow). */
.dx-section--vertical-top,
.dx-section--vertical-center,
.dx-section--vertical-bottom {
  display: flex;
  flex-direction: column;
}
.dx-section--vertical-top {
  justify-content: flex-start;
}
.dx-section--vertical-center {
  justify-content: center;
}
.dx-section--vertical-bottom {
  justify-content: flex-end;
}

/* ── Background image — rendered on a `::after` pseudo so it can be
   filtered / blended without affecting the section's content ─────────────

   The URL lives in the `--dx-section-bg-image` CSS variable (set inline by
   dx_render_section_inline_style). Putting the image on its own layer
   means user-authored CSS can target it with `%root%::after { filter:
   grayscale(1); }` — the filter applies only to the image, not to the
   text, buttons, or images inside the content. blur / brightness /
   hue-rotate / sepia all work the same way. The same trick is impossible
   when the image is set directly on the section element because filter
   inherits to all descendants.

   Size / position / attachment come from CSS variables too, with default
   fallbacks (cover / center / scroll) inside the var() — so the modifier
   classes only need to flip the variable, never re-declare the rule.

   `isolation: isolate` on the section creates a new stacking context so
   sibling sections can't interleave with our pseudo layers. */
.dx-section[style*="--dx-section-bg-image"] {
  position: relative;
  isolation: isolate;
}

.dx-section[style*="--dx-section-bg-image"]::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: var(--dx-section-bg-image);
  /* `cover` + `center` cover 95% of section-image use; the rare
       exception overrides via the section's Custom CSS pane (e.g.
       `%root%::after { background-size: contain; }`). No CSS variable
       indirection here — nothing flips it, so the literal is cleaner. */
  background-size: cover;
  background-position: center;
  background-attachment: var(--dx-section-bg-attachment, scroll);
  background-repeat: no-repeat;
}

/* Attachment is the only image-rendering knob still exposed —
   parallax is a design choice the user wants surfaced as a control,
   not buried in CSS.

   `fixed` produces a parallax-style effect — the image stays put while
   the section scrolls past it. Note: many mobile browsers ignore
   `background-attachment: fixed` for performance; we don't fight that
   — graceful degradation back to scroll is fine. */
.dx-section--bg-attachment-scroll {
  --dx-section-bg-attachment: scroll;
}
.dx-section--bg-attachment-fixed {
  --dx-section-bg-attachment: fixed;
}

/* ── Background overlay — semi-transparent layer above the bg image ──────
   Sits on `::before` (z-index 1) so it stacks ABOVE the bg `::after`
   (z-index 0) and BELOW the content (z-index 2). `pointer-events: none`
   so the overlay doesn't intercept clicks on links inside the section.
   Works without a bg-image too — a translucent wash over the mode-paired
   background colour can be useful for visual hierarchy.

   Color comes from `--dx-section-overlay-color` (set inline by the helper
   from a constrained role keyword — white / black / primary). Opacity has
   a three-step var() fallback: per-instance override → site-wide token
   default → 0.4 last-ditch. */
.dx-section--has-overlay {
  position: relative;
  isolation: isolate;
}

.dx-section--has-overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--dx-section-overlay-color);
  opacity: var(
    --dx-section-overlay-opacity,
    var(--dx-effect-overlay-opacity-default, 0.4)
  );
}

/* Content sits above both pseudo layers in any section that has either
   a bg image or an overlay — same `> .dx-container` selector both
   times so we only need one rule. */
.dx-section[style*="--dx-section-bg-image"] > .dx-container,
.dx-section--has-overlay > .dx-container {
  position: relative;
  z-index: 2;
}

/* ── Admin-bar offset — keeps sticky chrome below WP's logged-in bar ──────
   WordPress adds `.admin-bar` to <body> whenever the admin bar renders
   (logged-in users with the bar enabled). The bar is `position: fixed;
   top: 0; height: 32px` on desktop, `46px` below 600px viewport. Sticky
   elements pinning to the viewport top would otherwise overlap it.

   Exposed as a custom property so any sticky element can opt in with
   `top: var(--dx-admin-bar-offset)` rather than each writing its own
   `body.admin-bar` override. Defaults to 0px for logged-out users so
   the same CSS works in both contexts with no branch.

   Private chrome variable — NOT a user-settable design token, never
   stored in _dx_tokens, never surfaced on the Elegance settings UI. */
:root {
  --dx-admin-bar-offset: 0px;
}
body.admin-bar {
  --dx-admin-bar-offset: 32px;
}
@media (max-width: 600px) {
  /* WP's own breakpoint — the bar grows to 46px on small viewports. */
  body.admin-bar {
    --dx-admin-bar-offset: 46px;
  }
}

/* ── Site header / footer — stacking context above content ───────────────
   The theme-owned wrappers `<header class="dx-site-header">` and
   `<footer class="dx-site-footer">` are conceptually ABOVE all page
   content AND above any editor-chrome decoration applied to page
   sections.

   Two competing z-indices to clear:

   1. Page content — absolutely-positioned `.dx-image` elements
      inside Core blocks default to `z-index: auto`. Without an
      explicit z-index on the header, document order wins and
      images later in the DOM paint OVER a sticky header.

   2. The front-editor active-target marker
      (`.dx-fe-active-marker`, see assets/css/front-editor.css)
      paints a white `mix-blend-mode: difference` outline on the
      section currently bound to the sidebar. It carries
      `z-index: 998` so it can rise above section bg-image
      pseudos. A sticky header at z-index: 10 (the old value)
      lost to that marker — the user saw the difference outline
      bleeding over the header during scroll while editing.

   z-index: 1000 sits cleanly above both classes of in-page chrome
   and well below the WordPress admin bar (z-index: 99999), so the
   header lands between the two without fighting either. Picking a
   round number well above 998 leaves headroom — any future editor
   chrome can land somewhere in the 990-999 band without dragging
   the header back into a fight.

   `position: relative` is required for z-index to apply (z-index
   only works on positioned elements). It doesn't change the
   wrapper's layout — the wrapper still flows naturally and the
   user's `position: fixed` inside still resolves against the
   viewport. The relative-position is purely a stacking-context
   anchor.

   Why both header and footer: symmetry. Sticky footers are rare
   but legal (chat bars, "back to top" rails, dismissible banners),
   and the same z-index logic applies. */
.dx-site-header,
.dx-site-footer {
  position: relative;
  z-index: 1000;
}
