@layer ed-utilities {
  /* ed-prose applies hierarchical, asymmetric typographic rhythm to a block of
     long-form content. Builds on the native element styles (base/elements.css)
     instead of duplicating them. Em-based spacing scales when composed with
     ed-font-size-* utilities. The class and its element selectors are wrapped in
     :where() so every rule sits at 0,0,0 specificity — any utility class or element
     rule the author applies inside the container takes precedence without weight tricks.

     prose does NOT restyle type (font sizes, letter-spacing, numeric variants, etc.) —
     heading and body sizes come from the design system's native element styles. prose
     only adds the vertical rhythm and a reading column.

     NOTE: the rhythm scale and the 65ch reading column are carried over from
     WebAwesome as defaults, pending design-system fine-tuning. */
  :where(.ed-prose) {
    /* #region Tokens ~~~~~~~~~~~~ */

    /* Em-based rhythm scale: each token resolves at the use site, so margins scale
       with the element's own font-size. --ed-prose-rhythm-scale is a single knob to
       tighten or loosen everything at once. */
    --ed-prose-rhythm-scale: 1;
    --ed-prose-rhythm-2xs: calc(0.25em * var(--ed-prose-rhythm-scale));
    --ed-prose-rhythm-xs: calc(0.5em * var(--ed-prose-rhythm-scale));
    --ed-prose-rhythm-s: calc(0.75em * var(--ed-prose-rhythm-scale));
    --ed-prose-rhythm-m: calc(1em * var(--ed-prose-rhythm-scale));
    --ed-prose-rhythm-l: calc(1.5em * var(--ed-prose-rhythm-scale));
    --ed-prose-rhythm-xl: calc(2em * var(--ed-prose-rhythm-scale));
    --ed-prose-rhythm-2xl: calc(2.5em * var(--ed-prose-rhythm-scale));
    --ed-prose-rhythm-3xl: calc(3em * var(--ed-prose-rhythm-scale));

    --ed-prose-line-length: 65ch;

    max-inline-size: var(--ed-prose-line-length);
    /* #endregion */

    /* #region Headings ~~~~~~~~~~~~ */

    /* prose only sets the vertical rhythm here — heading type sizes come from the
       design system's native element styles, not from prose. */

    /* Headings hug what they introduce: generous above, tight below. */
    & :where(h1):has(+ *) {
      margin-block-end: var(--ed-prose-rhythm-s);
    }

    & * + :where(h2) {
      margin-block-start: var(--ed-prose-rhythm-2xl);
    }
    & :where(h2):has(+ *) {
      margin-block-end: var(--ed-prose-rhythm-s);
    }

    & * + :where(h3) {
      margin-block-start: var(--ed-prose-rhythm-xl);
    }
    & :where(h3):has(+ *) {
      margin-block-end: var(--ed-prose-rhythm-s);
    }

    & :where(h4, h5, h6):has(+ *) {
      margin-block-end: var(--ed-prose-rhythm-xs);
    }

    /* Back-to-back headings tighten so the second reads as subordinate.
       Enumerated explicitly — skipping more than two levels is rare enough that
       the default rhythm reads better. */
    & :where(h1 + h2, h1 + h3, h2 + h3, h2 + h4, h3 + h4, h4 + h5, h5 + h6) {
      margin-block-start: var(--ed-prose-rhythm-m);
    }
    /* #endregion */

    /* #region Body text & lists ~~~~~~~~~~~~ */

    /* Replaces the native rem-based --ed-content-spacing with em rhythm. */
    & :where(p, ul, ol, dl):has(+ *) {
      margin-block-end: var(--ed-prose-rhythm-l);
    }

    & :where(ul, ol) > :where(li):has(+ li) {
      margin-block-end: var(--ed-prose-rhythm-2xs);
    }

    & :where(dd):has(+ *) {
      margin-block-end: var(--ed-prose-rhythm-s);
    }
    /* #endregion */

    /* #region Captions ~~~~~~~~~~~~ */

    /* Em-based override keeps caption spacing in step with the prose container;
       the native styles set the visual style. */
    & :where(figcaption) {
      margin-block-start: var(--ed-prose-rhythm-xs);
    }
    /* #endregion */

    /* #region Inline code & long-word breaks ~~~~~~~~~~~~ */

    /* `anywhere` is the right trade-off in prose: long URLs and identifiers break
       instead of overflowing the column. */
    & :where(code, pre) {
      overflow-wrap: anywhere;
    }
    /* #endregion */

    /* #region Section breaks & major non-text blocks ~~~~~~~~~~~~ */

    /* <hr>'s own margin defines the gap; the next heading shouldn't double-stack. */
    & :where(hr) {
      margin-block: var(--ed-prose-rhythm-3xl);
    }
    & :where(hr + :is(h1, h2, h3, h4, h5, h6)) {
      margin-block-start: 0;
    }

    /* Major non-text blocks get more air than running prose, but headings still
       hug — so the top-margin rule excludes the heading-to-block case (handled
       separately below with a smaller gap). */
    & :where(pre, figure, table, blockquote, ed-notification, details):has(+ *) {
      margin-block-end: var(--ed-prose-rhythm-xl);
    }

    & :where(:not(h1, h2, h3, h4, h5, h6) + :is(pre, figure, table, blockquote, ed-notification, details)) {
      margin-block-start: var(--ed-prose-rhythm-xl);
    }

    & :where(:is(h1, h2, h3, h4, h5, h6) + :is(pre, figure, table, blockquote, ed-notification, details)) {
      margin-block-start: var(--ed-prose-rhythm-m);
    }
    /* #endregion */
  }

  /* Reverts the block spacing ed-prose applies so utilities like ed-cluster still
     work inside the opt-out subtree. `.ed-not-prose *` at 0,1,0 wins over ed-prose's
     0,0,0 element rules. */
  .ed-not-prose,
  .ed-not-prose * {
    margin-block-start: revert-layer;
    margin-block-end: revert-layer;
  }
}
