/* @canonical/styles-typography — Classic engine
 *
 * Requires JS font extraction via `extractFontData` and the following :root
 * variables:
 *   --ascender, --descender, --units-per-em
 *
 * Consumer contract:
 *   :root {
 *     --baseline-height: <length>;   (default 0.25rem with tokens.css loaded;
 *                                     required without it)
 *     --ascender: <number>;
 *     --descender: <number>;
 *     --units-per-em: <number>;
 *   }
 *   element { --font-size: <length>; --line-height: <length>; }
 *   (fallback: --line-height-multiplier: <number> if --line-height is unset)
 *
 * Browser floor: Chrome 125, Safari 15.4, Firefox 118, all three set by
 * `mod()`, the only feature this engine needs. It reaches further than the cap
 * engine in Safari, which needs the `cap` unit as well; what it buys beyond
 * that is not reach but control, since the metrics are numbers you supply rather
 * than the font the browser happens to use.
 *
 * The grid unit:
 *   --baseline-height may be a length in any unit — 0.5rem and 8px are both
 *   fine, and rem is the usual choice because the grid then follows the reader's
 *   own font size. This file reads it bare, without a fallback of its own: the
 *   default lives once, in tokens.css's `ds.tokens` block, as
 *   `:where(:root) { --baseline-height: 0.25rem }`. Zero weight, so any real
 *   declaration wins — @canonical/styles declares it at :root in spacing.css,
 *   and an application may declare it on any element.
 *
 *   Linked on its own this file has no default. No engine here imports
 *   anything at all — the composition lives in index.css — so a
 *   stylesheet taking one engine alone either declares --baseline-height itself
 *   or imports `./tokens.css` beside it. Without one of those the nudges here
 *   resolve to nothing and the engine does not run.
 *
 * Layers:
 *   Every rule below sits in `ds.typography`. Unlayered, these rules outranked
 *   every layered rule in the page whatever the selectors on either side, and
 *   tied with an application's own `p` rule on specificity so that load order
 *   decided the winner one property at a time. In a layer the design system
 *   loses to the application's own layered CSS, deliberately, and beats the
 *   layers below it, also deliberately.
 *
 *   The rules select elements by name, so they apply to the whole document,
 *   which is what a design system's typography is for.
 */

@layer ds.typography {
  :root {
    /* Agnostic values */
    --natural-line-height: calc((var(--ascender) - var(--descender)));
    --line-height-scale: calc(
      var(--natural-line-height) *
      1rem /
      var(--units-per-em)
    );
    --ascender-scale: calc(var(--ascender) * 1rem / var(--units-per-em));
  }

  /* Reset */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  .p,
  .code {
    margin-block: 0;
  }

  /* Baseline alignment — classic engine
   *
   * Both nudges use padding (not margin) so the element's border-box block size
   * participates correctly in flex and grid layouts. margin-block-end is reserved
   * solely for --space-after (editorial element-owned spacing, default 0).
   */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  .p,
  .code {
    --computed-line-height: var(
      --line-height,
      calc(var(--baseline-height) * var(--line-height-multiplier))
    );
    line-height: var(--computed-line-height);
    font-size: var(--font-size);

    --line-height-scale: calc(
      var(--natural-line-height) *
      var(--font-size) /
      var(--units-per-em)
    );
    --ascender-scale: calc(
      var(--ascender) *
      var(--font-size) /
      var(--units-per-em)
    );

    --baseline-position: calc(
      ((var(--computed-line-height) - var(--line-height-scale)) / 2) +
      var(--ascender-scale)
    );
    --start-nudge: calc(
      var(--baseline-height) -
      mod(var(--baseline-position), var(--baseline-height))
    );
    --end-nudge: calc(var(--baseline-height) - var(--start-nudge));
  }

  /* Block-level baseline nudges only — bare inline `.code` is excluded so it
   * does not double-pad inside a paragraph. Combine `.p.code` for a code block. */
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p,
  .p {
    /* Nudges as padding — inside the box for flex/grid compatibility */
    padding-block-start: var(--start-nudge);
    padding-block-end: var(--end-nudge);

    /* Element-owned spacing — only active in .editorial contexts */
    margin-block-end: calc(var(--space-after, 0) * var(--baseline-height));
  }
}
